OS X 10.10 から JavaScript for Automation (JXA) が利用可能になった。ここでは、その Automation 機能をシェルスクリプトとして利用するステップを記述する。
シェルスクリプトとして利用するので、その前提はターミナルでテキストファイルとして記述する。その際にはファイルを実行するコマンドが何かを記載するシバンが必要。JXA の場合は osascript
コマンドを利用する。
#!/usr/bin/env osascript -l JavaScript
コマンドを終了させる為にライブラリーをインポートして $.exit()
を利用可能にする。
ObjC.import('stdlib')
error = 0
$.exit(error)
引数を扱う方法は幾つかある。その中で一番単純な方法はメイン処理の run()
を定義すること。その関数の引数がコマンドラインの引数となる。
function run(args) {
args.forEach(function(arg, idx) {
switch (arg) {
case 'foo':
break
default:
break
}
})
}
JXA は外部コマンドを実行する doShellScript()
が用意されている。利用は以下の手順で。
app = Application.currentApplication()
app.includeStandardAdditions = true
cmd = 'ls -l /usr/bin | sort | head -n 5'
app.doShellScript(cmd, {
administratorPrivileges: false,
withPrompt: '',
alteringLineEndings: false
})
JXA は Objective-Cオブジェクトを簡単に呼べるので、 NSFileHandle
を利用する。
std = {
input: function() {
var input = $.NSFileHandle.fileHandleWithStandardInput.availableData
return $.NSString.alloc.initWithDataEncoding(input, $.NSUTF8StringEncoding).js
},
output: function(str) {
var data = $(str).dataUsingEncoding($.NSUTF8StringEncoding)
if (data) $.NSFileHandle.fileHandleWithStandardOutput.writeData(data)
},
error: function(str) {
var data = $(str).dataUsingEncoding($.NSUTF8StringEncoding)
if (data) $.NSFileHandle.fileHandleWithStandardError.writeData(data)
}
}
console
を拡張してもいいかもしれない。
console.input = function() {
var input = $.NSFileHandle.fileHandleWithStandardInput.availableData
return $.NSString.alloc.initWithDataEncoding(input, $.NSUTF8StringEncoding).js
}
console.output = function(str) {
var data = $(str).dataUsingEncoding($.NSUTF8StringEncoding)
if (data) $.NSFileHandle.fileHandleWithStandardOutput.writeData(data)
}
console.error = function(str) {
var data = $(str).dataUsingEncoding($.NSUTF8StringEncoding)
if (data) $.NSFileHandle.fileHandleWithStandardError.writeData(data)
}
使い方の例:
function run(args) {
console.log(`arguments: ${args}`)
do {
std.output('input answer: ')
var input = std.input()
console.log( input )
} while (input != '\n')
std.output('これはスタンダードメッセージ\n')
std.error('これはエラーメッセージ')
}
具体的なサンプルとして jupyter
のコントロールコマンド jupyterctl
を挙げる。
#!/usr/bin/env osascript -l JavaScript
ObjC.import('stdlib')
app = Application.currentApplication()
app.includeStandardAdditions = true
function sh(command, promptText) {
try {
return app.doShellScript(command, {
administratorPrivileges: promptText ? true : false,
withPrompt: promptText ? promptText : '',
alteringLineEndings: false
})
} catch(e) {
console.log(e)
return null
}
}
function start() {
return sh("/bin/bash --login -c 'cd ~/usr/workspace/anyenv/python/; jupyter-notebook --no-browser >> ~/usr/log/jupyter.log 2>&1' &>/dev/null &")
}
function stop() {
return sh("ps aux | grep [j]upyter-notebook | awk '{print $2}' | sort | xargs kill")
}
function status() {
return sh('ps aux | grep [j]upyter-notebook') != null
}
function run(args) {
var error = 0
if (args.length != 1) {
args = ['help']
}
args.forEach(function(arg, i) {
switch (arg) {
case 'start':
error = Jupyter.start() != null ? 0 : 1
break
case 'stop':
error = Jupyter.stop() != null ? 0 : 1
break
case 'restart':
if (Jupyter.stop() == null) { error = 1; break }
delay(1)
error = Jupyter.start() != null ? 0 : 1
break
case 'status':
var status = Jupyter.status(); if (status == null) {
error = 1; break
}
if (status) {
console.log('jupyter is running...')
} else {
console.log('jupyter is stop.')
}
break
default:
console.log('Usage: jupyterctl {start|stop|restart|status|help}')
break
}
})
$.exit(error)
}
app = Application.currentApplication()
app.includeStandardAdditions = true
function sh(command, promptText) {
try {
return app.doShellScript(command, {
administratorPrivileges: promptText ? true : false,
withPrompt: promptText ? promptText : '',
alteringLineEndings: false
})
} catch(e) {
console.log(e)
return null
}
}
CommonLib = Library('Common')
sh = CommonLib.sh
function start() {
return sh("/bin/bash --login -c 'cd ~/usr/workspace/anyenv/python/; jupyter-notebook --no-browser >> ~/usr/log/jupyter.log 2>&1' &>/dev/null &")
}
function stop() {
return sh("ps aux | grep [j]upyter-notebook | awk '{print $2}' | sort | xargs kill")
}
function status() {
return sh('ps aux | grep [j]upyter-notebook') != null
}
#!/usr/bin/env osascript -l JavaScript
ObjC.import('stdlib')
Jupyter = Library('Jupyter')
function run(args) {
var error = 0
if (args.length != 1) {
args = ['help']
}
args.forEach(function(arg, i) {
switch (arg) {
case 'start':
error = Jupyter.start() != null ? 0 : 1
break
case 'stop':
error = Jupyter.stop() != null ? 0 : 1
break
case 'restart':
if (Jupyter.stop() == null) { error = 1; break }
delay(1)
error = Jupyter.start() != null ? 0 : 1
break
case 'status':
var status = Jupyter.status(); if (status == null) {
error = 1; break
}
if (status) {
console.log('jupyter is running...')
} else {
console.log('jupyter is stop.')
}
break
default:
console.log('Usage: jupyterctl {start|stop|restart|status|help}')
break
}
})
$.exit(error)
}