Mac OSXの純正アプリケーション「Automator」では、自動でいくつかの処理を行うことが出来ます。

develop or drink ?
 develop or drink ? ©

その中でも新規ワークフローでjavascriptを実行を使って、AppleScriptで利用できるjavascript 1.0を書いて実行することができます。ちなみに、ES6(ES2015)は使えません。

今回はjavascriptを使って、ターミナルを起動し、コマンドを自動入力させる方法をまとめてみたいと思います。

検索欄で「javascript」と入力すると、javascriptを実行が選択できる
検索欄で「javascript」と入力すると、javascriptを実行が選択できる ©

目次

スクリプトエディタでjavascriptをデバッグする

まず、デバッグ環境について説明したいと思います。

Automatorで記述していくjavascriptは、Mac OSXの純正アプリケーション「スクリプトエディタ」でも実行やデバッグすることができます。

スクリプトエディタでデバッグできる
スクリプトエディタでデバッグできる ©

javascriptを使う際には、左上にあるAppleScriptJavascriptに予め変更しておきましょう。

また、console.log()で値を確認したい時には、スクリプトエディタ > 表示 > ログを隠すでログを表示させる事ができるので、そこで値を確認することができます。

Automatorでターミナルを起動する

ターミナルをAutomatorから起動させるには、以下のスクリプトを記述します。

terminal-run.jsa
1
2
3
4
5
6
function run(input, parameters) {
var Terminal = Application('Terminal')
Terminal.activate()
var terW1 = Terminal.windows[0]
return input;
}

terW1で起動したターミナルウインドウを取得します。

また、ターミナルを一旦閉じてしまうと、ターミナルが起動した状態でありながら、ターミナル・ウインドウが1つもない状態となってしまいます。こういった状態の場合に、上記のコードを実行するとエラーとなります。

このような場合には、ウインドウのプロパティを取得してエラーが発生した際に、ターミナルの新規ウインドウを作成すると、回避する事ができます。

terminal-run.jsa
1
2
3
4
5
6
7
8
9
10
11
12
function run(input, parameters) {
var Terminal = Application('Terminal')
Terminal.activate()
var terW1 = Terminal.windows[0]
try{
console.log(terW1.selectedTab.properties())
}catch(e){
Terminal.doScript("echo 'Hi terminal window 1'");
terW1 = Terminal.windows[0]
}
return input;
}

ターミナルのコマンド実行中は待機する

ターミナルのコマンドが実行中に、次のコマンドを入力するとエラーが発生します。

その為、ターミナルが実行中はスクリプトの実行を待機する必要があります。下記のサンプルコードでは、コマンドが終了するまでスクリプトを停止させるwaitDelayを追加してみました。

terminal-run2.jsa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function run(input, parameters) {
var Terminal = Application('Terminal')
Terminal.activate()
var terW1 = Terminal.windows[0]
try{
console.log(terW1.selectedTab.properties())
}catch(e){
Terminal.doScript("echo 'Hi terminal window 1'");
terW1 = Terminal.windows[0]
}

// コマンド実行完了まで待機
waitDelay(terW1)

// waitDelayの後でコマンドを入力させる...
return input;
}

// コマンド実行中は1秒以上待機させ、スクリプトの実行を一時停止させる関数。
function waitDelay(inTerminalWindow){
delay(1)
while( inTerminalWindow.selectedTab.busy() ){
delay(1)
console.log("待機中")
}
}

Automatorからターミナルコマンドを実行する

Automatorからコマンドを実行するには、doScript()を使います。

例えば、ディレクトリを変更する場合には以下のようなコードを記述していきます。

terminal-cd.jsa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function run(input, parameters) {
var Terminal = Application('Terminal')
Terminal.activate()
var terW1 = Terminal.windows[0]
try{
console.log(terW1.selectedTab.properties())
}catch(e){
Terminal.doScript("echo 'Hi terminal window 1'");
terW1 = Terminal.windows[0]
}
waitDelay(terW1)

//ディレクトリを変更するコマンドを実行
Terminal.doScript( 'cd \"$HOME/Desktop/your-dir\"' , {in: terW1} )

waitDelay(terW1)
return input;
}

// コマンド実行中は1秒待機させ、スクリプトの実行を一時停止させる関数。
function waitDelay(inTerminalWindow){
delay(1)
while( inTerminalWindow.selectedTab.busy() ){
delay(1)
console.log("待機中")
}
}

なお、コマンドラインの基礎に関しては、以下をご覧ください。

Automatorから複数のコマンドを実行していく

順番に2件以上のコマンドを実行していくには、waitDelay()を挟みながら、doScript()メソッド内でコマンドを書いていきます。

例えば、Automatorでnode.jsのプロジェクトを最新に更新して、コードを実行させるには次のように記述していきます。

terminal-commands.jsa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
function run(input, parameters) {
var Terminal = Application('Terminal')
Terminal.activate()
var terW1 = Terminal.windows[0]
try{
console.log(terW1.selectedTab.properties())
}catch(e){
Terminal.doScript("echo 'Hi terminal window 1'");
terW1 = Terminal.windows[0]
}
waitDelay(terW1)

//ディレクトリを変更するコマンドを実行
Terminal.doScript( 'cd \"$HOME/Desktop/your-dir\"' , {in: terW1} )
waitDelay(terW1)

// プロジェクトを更新
Terminal.doScript( 'git pull origin master' , {in: terW1} )
waitDelay(terW1)

// node.jsのバージョンを指定
Terminal.doScript( 'nvm use 4.3.0' , {in: terW1} )
waitDelay(terW1)

// ndoe.jsを実行させる
Terminal.doScript( 'node index.js' , {in: terW1} )
waitDelay(terW1)

return input;
}

// コマンド実行中は1秒待機させ、スクリプトの実行を一時停止させる関数。
function waitDelay(inTerminalWindow){
delay(1)
while( inTerminalWindow.selectedTab.busy() ){
delay(1)
console.log("待機中")
}
}

Automatorから起動したターミナルでキーボード入力させる

Automatorから起動したターミナルでキーの入力(例えば^Cなど)させるには、次のgistが参考になります。

gist
jiaaro/hotkey_helpers.js

上記のコードを利用すると、press()でキーボード入力を行うことができます。

例えば、以下のコードでは、サーバーを起動させ、5分後にサーバーを停止させるサンプルコードです。

terminal-server.jsa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
function run(input, parameters) {
var Terminal = Application('Terminal')
Terminal.activate()
var terW1 = Terminal.windows[0]
try{
console.log(terW1.selectedTab.properties())
}catch(e){
Terminal.doScript("echo 'Hi terminal window 1'");
terW1 = Terminal.windows[0]
}
waitDelay(terW1)

//ディレクトリを変更するコマンドを実行
Terminal.doScript( 'cd \"$HOME/Desktop/your-dir\"' , {in: terW1} )
waitDelay(terW1)
// サーバーを起動させる
Terminal.doScript( 'hexo s' , {in: terW1} )

// 5分後にサーバーを停止させる
delay(300)
press("^c")

return input;
}

// コマンド実行中は1秒待機させ、スクリプトの実行を一時停止させる関数。
function waitDelay(inTerminalWindow){
delay(1)
while( inTerminalWindow.selectedTab.busy() ){
delay(1)
console.log("待機中")
}
}

// hot key helpers
// code by https://gist.github.com/jiaaro/c95e89ea5cacc6906040
var key_codes = {
"→": 124,
"←": 123,
"↑": 126,
"↓": 125,
"⏎": 36
};

var modifiers = {
"⌘": "command down",
"^": "control down",
"⌥": "option down",
"⇧": "shift down"
};

function press(hotkey) {
var using = [];

while (hotkey.length > 1) {
if (modifiers[hotkey[0]] == undefined) {
throw new Error(hotkey[0] + " is not a recognized modifier key");
}

using.push(modifiers[hotkey[0]]);
hotkey = hotkey.slice(1);
}

if (key_codes[hotkey] != undefined) {
sys.keyCode(key_codes[hotkey], {using: using});
}
else {
sys.keystroke(hotkey.toLowerCase(), {using: using});
}
}
function type(text) {
for (var i=0; i < text.length; i++) {
sys.keystroke(text[i]);
}
}
function menu_item() {
if (!arguments.length) return;

var process = sys.processes.whose({"frontmost": true})[0];
var menu_bar = process.menuBars[0].menuBarItems[arguments[0]];

var menu_item = menu_bar;
for (var i=1; i < arguments.length; i++) {
menu_item = menu_item.menus[0].menuItems[arguments[i]];
}
menu_item.click();
}

Automatorから複数のターミナルウインドウを新規作成する

複数の新規ターミナルウインドウを作成するには、doScript()に第2引数(操作したいウインドウ)を指定せずに実行します。

以下は、3つのターミナルウインドウを作成するサンプルコードです。

terminal-create-windows.jsa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function run(input, parameters) {
var Terminal = Application('Terminal')
Terminal.activate()

var terW1 = Terminal.windows[0]
try{
console.log(terW1.selectedTab.properties())
}catch(e){
Terminal.doScript("echo 'Hi terminal window 1'");
terW1 = Terminal.windows[0]
}
waitDelay(terW1)

// 2つ目の新規ターミナル・ウインドウを作成
Terminal.doScript("echo 'Hi terminal window 2'");
var terW2 = Terminal.windows[0]
waitDelay(terW2)

// 3つ目の新規ターミナル・ウインドウを作成
Terminal.doScript("echo 'Hi terminal window 3'");
var terW3 = Terminal.windows[0]
waitDelay(terW3)

return input;
}

// コマンド実行中は1秒待機させ、スクリプトの実行を一時停止させる関数。
function waitDelay(inTerminalWindow){
delay(1)
while( inTerminalWindow.selectedTab.busy() ){
delay(1)
console.log("待機中")
}
}

各ウインドウに対して、コマンドを実行するには次のように記述します。

ここで注意点ですが、手前のウインドウから奥のウインドウに向かって、0番目〜2番目となっているので、ウインドウの変数を扱う際に気をつけて下さい。

terminal-create-windows2.jsa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
function run(input, parameters) {
var Terminal = Application('Terminal')
Terminal.activate()

var terW1 = Terminal.windows[0]
try{
console.log(terW1.selectedTab.properties())
}catch(e){
Terminal.doScript("echo 'Hi terminal window 1'");
terW1 = Terminal.windows[0]
}
waitDelay(terW1)

Terminal.doScript("echo 'Hi terminal window 2'");
var terW2 = Terminal.windows[0]
waitDelay(terW2)

Terminal.doScript("echo 'Hi terminal window 3'");
var terW3 = Terminal.windows[0]
waitDelay(terW3)


// 手前のウインドウから奥のウインドウに向かって、0番目〜2番目となっているので注意
terW3 = Terminal.windows[0]
terW2 = Terminal.windows[1]
terW1 = Terminal.windows[2]


Terminal.doScript( 'echo "created window 1"' , {in: terW1} )
waitDelay(terW1)

Terminal.doScript( 'echo "created window 2"' , {in: terW2} )
waitDelay(terW2)

Terminal.doScript( 'echo "created window 3"' , {in: terW3} )
waitDelay(terW3)

return input;
}

// コマンド実行中は1秒待機させ、スクリプトの実行を一時停止させる関数。
function waitDelay(inTerminalWindow){
delay(1)
while( inTerminalWindow.selectedTab.busy() ){
delay(1)
console.log("待機中")
}
}

ということで

今回はMac OSX純正アプリの「Automator」から実行できるAppleScript対応のjavascriptで、ターミナルを実行する方法をまとめました。

コマンドをアプリ1クリックで、自動的に色々な処理を走らせることができて大変便利です。サンプルコードではコマンドを逐次実行する方法や、複数ウインドウの作成方法を解説しました。よろしければ参考にしてください。

参考資料

以下の記事を一部参考にさせていただきました。ありがとうございます。

Qiita
Terminalで「すぐに消せ」ごっこ (Mac, JXA)