Skip to content

Instantly share code, notes, and snippets.

@c9iim
Last active August 29, 2015 14:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save c9iim/1d8e02cc75d19e9a85c9 to your computer and use it in GitHub Desktop.
Save c9iim/1d8e02cc75d19e9a85c9 to your computer and use it in GitHub Desktop.

AppleScriptを書く準備

OSXに最初からAppleScriptエディタという専用のエディタがインストールされています。コード補完や実行結果の表示など優れた機能を有していますのでこちらを使用することをおすすめ。アプリケーションフォルダのユーティリティの中にあります。

hello world

display dialog "hello world"

AppleScriptエディタのツールバーの実行ボタン(Command + R)でコンパイル&実行が行われます。正しい文法で記述されているかはコンパイルボタン(Command + K)で確認できます。

あとAppleScriptらしいhello worldはこんな感じで。

say "hello world"

基本的な考え方

AppleScriptは「オブジェクトに対してメッセージを送る」という考えでコードを書くのが基本。いわゆるオブジェクト指向言語というものです。アプリケーションはオブジェクトの一つと言えます。特徴的なのは比較的自然言語に近い形でコードが書けるところです。

tell referenceToObject to statement

例:iTunesにプレイリスト名"トップ 25"の再生を指示。

tell application "iTunes" to play the playlist named "トップ 25"

例:現在のアプリケーションに"hello world"ダイアログ表示を指示。

tell current application to display dialog "hello world"

tell current application to は省略可能。よく目にするコードはこの形。

display dialog "hello world"

これはAppleScriptエディタにダイアログ表示させるという指示です。これをiTunesにさせてみます。

tell application "iTunes" to display dialog "hello world"

するとiTunesがアクティブになりダイアログが表示されます。これはAppleScriptエディタではなくiTunesがダイアログを表示したという事です。本当にiTunesのダイアログなのかを確認するにはドックのiTunesアイコンからメニューを開きiTunesを隠してみてください。ダイアログも一緒に隠されます。iTunesを表示すると再びダイアログも表示されます。

このようにAppleScriptはオブジェクトに対して何らかの操作を指示することが簡単に出来る仕組みです。

このドキュメントはオブジェクトの操作を覚える前にまずAppleScriptの文法をごく簡単に学ぶことを目的として書いていますので、以下の対象とするオブジェクトはAppleScriptエディタとします。

表示

単に結果を知るためだけならわざわざ表示させる必要はありません。エディタの下にある結果エリアに表示されます。編集エリアにて1 + 1と入力し実行してみてください。計算結果が表示されます。

何かを明示的に表示する場合はdisplay dialogコマンドを使います。

display dialog "hello (again)"

デバッグの為に値を表示したい場合はlogコマンドを使います。こちらは返された値イベントのエリアで表示されます。

log "hello (again)"

hello worldでも挙げましたが、sayコマンドを使い指定した内容を読み上げさせることもできます。

say "think different."

コメント

一行コメント

-- this is comment.

ブロックコメント

(*
 これはブロックコメントです。
 複数行のコメントはこちらを使用します。
 *)

変数

変数は前もって宣言する必要はありません。値を代入する記述を行えばすぐに変数を使用できます。型は特に指定する必要はありませんが明示することも可能です。

値を変数に代入するにはsetコマンドを使用します。

-- 変数oneに数値の1を代入
set one to 1

-- 変数fooに文字列"foo"を代入
set foo to "foo"

-- 変数barに真を代入
set bar to true

-- 変数bazに文字列"baz"を文字列であることを明示して代入。
set baz to "baz" as text

copyコマンドで変数に値を代入するも出来ます。

copy 1 to one

getコマンドで変数から値を取得します。getを省略し単に変数名だけでも値を取得できます。

set foo to "foo"
get foo
--> "foo"

a reference toコマンドは左辺の値を求めるのではなく右辺の参照として処理します。

set one to 1
set ichi to a reference to one
get ichi -- get oneと同義
ichi + ichi
--> 2

missing valueを代入することが出来ます。missing valueは初期化されていない値を意味します。

set foo to missing value
get foo
--> missing value

localコマンドでスコープ内に有効な変数の宣言、globalコマンドで全てのスコープに対して有効な変数の宣言ができます。

local foo -- defines one variable
local bar,baz -- defines 2 variables

global hoge
global fuga, hogera

数値

数値を表すnumber、整数のinteger、実数のrealの3つが型として使用できます。

set a to 1
set b to 1.234

set b to b as integer
--> 1

set a to a as real
--> 1.0

set a to "100"
set b to a as number
--> 100

四則演算

-- 加算
1 + 1
--> 2

-- 減算
1 - 1
--> 0

-- 乗算
1 * 1
--> 1

-- 除算
10 / 3
--> 3.333333333333

--
10 div 3
--> 3

-- 余り
10 mod 3
--> 1

インクリメント、デクリメントはありません。

文字列

文字列はダブルクォートで囲みます。enterキーで文字列を改行させることができます。タブやリターン、改行などの特殊記号は\t、\r、\nと書きます。

set str to "this is test."
set str to "
    this
    is
    test.
"
set str to "\tthis is \ntest.\r"

注意:AppleScriptエディタの環境設定で特殊文字を展開する場合があります。

文字列操作

-- 結合
"aaa" & "bbb"
--> "aaabbb"

-- 分割
set AppleScript's text item delimiters to {","}
text items of "aaa,bbb,ccc"-> {”aaa”, ”bbb”, “ccc”}

-- 長さ
length of "あいうえお"-> 5
count "あいうえお"
--> 5

-- 切り出し- 13文字目
text 1 thru 3 of "ABCDEFG"-> “ABC”

–- 4~末尾
text 4 thru -1 of "ABCDEFG"-> “DEFG”

–- 4~末尾の1つ前
text 4 thru -2 of "ABCDEFG"-> “DEF”

-- delimitersの使用例
set tempDelimiters to AppleScript's text item delimiters
set filePath to "Macintosh HD:Users:hoge:Desktop:example.applescript"
set AppleScript's text item delimiters to {":"}
get text items of filePath
--> {"Macintosh HD","Users","hoge","Desktop","example.applescript"}
get first text item of filePath
--> "Macintosh HD"
get last text item of filePath
--> "example.applescript"
get text item 3 of filePath
--> "hoge"
repeat with anItem in text items of filePath
    log anItem
end repeat
(*
--> "Macintosh HD"
    "Users"
    "hoge"
    "Desktop"
    "example.applescript"
*)
copy tempDelimiters to AppleScript's text item delimiters

AppleScriptには標準の検索・置換機能はありません。delimitersを使用して独自に検索機能を書く必要があります。操作対象のアプリケーションによっては検索・置換コマンドが用意されているのでそちらを使うのが簡単です。

リスト

AppleScriptで配列はリストと呼びます。

-- リスト
{"foo", "bar", "baz"}

-- リストの入れ子
{{1, 2, 3}, {2, 3, 4}, {3, 4, 5}}

-- リストの連結
{"foo"} & {"bar", "baz"}
--> {"foo", "bar", "baz"}

-- 要素数
count {"foo", "bar", "baz"}
--> 3

-- 要素数 整数のみ
count integers in {"foo", "bar", "baz", 1, 2, 3}

-- 3番目の要素
item 3 of {"foo", "bar", "baz"}
--> "baz"

-- ランダムな要素
some item of {"foo", "bar", "baz"}

-- 4〜6の要素
items 4 thru 6 of {"foo", "bar", "baz", 1, 2, 3}
--> {1, 2, 3}

-- 要素の並びを反転
reverse of {"foo", "bar", "baz"}
--> {"baz", "bar", "foo"}

-- 先頭を除いた残りの要素
rest of {"foo", "bar", "baz"}
--> {"bar", "baz"}

リストの操作は直接影響を及ぼしません。実際に内容を変更したい場合はsetやcopyを用いて代入する必要があります。

レコード

AppleScriptの連想配列はレコードと呼ばれます。

-- レコード
{name:"Steve", height:74.5, weight:170}

-- キーがnameの要素
name of {name:"Steve", height:74.5, weight:170}

-- 連結
{name:"Steve", height:74.5, weight:170} & {lastName:"Jobs"}

-- 要素数
count {name:"Steve", height:74.5, weight:170}

-- 要素数 値が文字列のみ
count text in {name:"Steve", height:74.5, weight:170}

-- ランダムな要素
some item of {name:"Steve", height:74.5, weight:170}

レコードの操作は直接影響を及ぼしません。実際に内容を変更したい場合はsetやcopyを用いて代入する必要があります。

-- 要素への代入
copy 210 to weight of {name:"Steve", height:74.5, weight:170}

-- 要素の追加
set aRecord to {name:"Steve", height:74.5, weight:170}
copy aRecord & {lastName:"Woz"} to aRecord

レコードから任意の要素を削除するコマンドはありません。新たにレコードを構築し直して代入する必要があります。

制御文

制御文は他の言語に比べて貧弱かもしれません。条件分岐でswitchに相当するものはなくif文のみ。繰り返しもrepeatだけです。

if文

if 条件式 then

else if 条件式 then

else

end if

repeat文

-- 指定した回数の処理を行う
repeat 繰り返す回数 times

end repeat


-- 指定範囲内で処理を繰り返す
repeat with 変数 from 開始 to 終了

end repeat


-- リストを順に繰り返し
repeat with 変数 in リスト

end repeat


-- 条件一致まで繰り返す
repeat while 条件式

end repeat


-- 無限ループ
repeat 

end repeat


-- ループから抜ける
repeat

    if 条件式 then exit repeat

end repeat

演算子

演算子の一覧です。AppleScriptは自然言語に近い記述ができるよう表現方法が豊富です。しかし最初からそれを全て覚えて書くのは大変。ここでは自分がよく使うものだけをピックアップしました。詳しくはオフィシャルドキュメントを参照してください。

AppleScript Language Guide - Operators Reference

AppleScript operator 説明
=, is 等しい
is not 等しくない
and かつ
or または
> より大きい
< より小さい
>= 以上
<= 以下

例外処理

-- 基本形
try

on error

end try

-- エラーハンドリングの例
try
    error number -10004 -- エラー番号-10004のエラーを発生
on error message number n
    if n = -128 then (*ユーザによってキャンセルされました。*)
        -- do something
    else if n = -10004 then (*アクセス権の違反が起きました。*)
        -- do something
    else -- 想定外のエラーは再度エラーとする
        log message
        error number n
    end if
end try

サブルーチン

AppleScriptではサブルーチンをハンドラと言います。

-- 基本
on helloWorld()
    display dialog "Hello World"
end

helloWorld()


-- 位置パラメーター形式のハンドラ
on greeting(message)
    display dialog message
end

greeting("hello, again")


-- ラベル付きパラメーター形式のハンドラ   
to findNumbers of numberList above minLimit given rounding:roundBoolean
        set resultList to {}
        repeat with i from 1 to (count items of numberList)
            set x to item i of numberList
            if roundBoolean then -- round the number
                -- Use copy so original list isn’t modified.
                copy (round x) to x
            end if
            if x > minLimit then
                set end of resultList to x
            end if
        end repeat
        return resultList
end findNumbers

set myList to {2, 5, 19.75, 99, 1}
findNumbers of myList above 19 given rounding:true
--> {20, 99}


-- 定義済みラベル付きパラメーター形式のハンドラ
on rock around the clock
    display dialog (clock as text)
end rock

rock around the current date -- 現在日時を表示

ハンドラのパラメーターにはいくつかの種類がありますが、これはハンドラを使用する際の記述の可読性を高める目的で使い分けます。すべてを位置パラメーター形式で定義してもかまいません。またonの代わりにtoを用いて定義することも可能です。やはりこちらも可読性を高めるのが目的でどちらを使用しても機能的な差異はありません。

スクリプトオブジェクト

AppleScriptはプロトタイプベースのオブジェクト指向言語です。オブジェクトの定義がそのままインスタンスとなり即利用できます。AppleScriptで定義できるオブジェクトはスクリプトオブジェクトといいます。スクリプトオブジェクトにはプロパティとハンドラを定義できますが、その他にコマンドやハンドラの呼び出しを列挙できます。継承はparentプロパティを使い簡単に行えます。

-- 基本形
script John
    property HowManyTimes : 0
    to sayHello to someone
        set HowManyTimes to HowManyTimes + 1
        return "Hello " & someone
    end sayHello
end script

tell John to sayHello to "Herb" --result: "Hello Herb"
tell John
    sayHello to "Rose"
    sayHello to "Grace"
end tell


-- ハンドラによるスクリプトオブジェクトの初期化と生成
on makePoint(x, y)
    script thePoint
        property xCoordinate:x
        property yCoordinate:y
    end script
    return thePoint
end makePoint

set aPoint to makePoint(0,0)
get xCoordinate of aPoint  --result: 0
get yCoordinate of aPoint  --result: 0

set myPoint to makePoint(10,20)
get xCoordinate of myPoint  --result: 10
get yCoordinate of myPoint  --result: 20
get xCoordinate of aPoint  --result: 0
get yCoordinate of aPoint  --result: 0

tell myPoint
    set xCoordinate to 20
    set yCoordinate to 40
    get xCoordinate --result: 20
    get yCoordinate --result: 40
end tell


-- 継承
script Alex
    on sayHello()
        return "Hello, " & getName()
    end sayHello
    on getName()
        return "Alex"
    end getName
end script
script AlexJunior
    property parent : Alex
    on getName()
        return "Alex Jr"
    end getName
end script
-- Sample calls to handlers in the script objects:
tell Alex to sayHello() --result: "Hello, Alex"
tell AlexJunior to sayHello() --result: "Hello, Alex Jr."
tell Alex to getName() --result: "Alex"
tell AlexJunior to getName() --result: "Alex Jr"

script John
    property vegetable : "Spinach"
end script
script JohnSon
    property parent : John
end script
set vegetable of John to "Swiss chard"
vegetable of JohnSon
--result: "Swiss chard"


-- オーバーライドしたハンドラから親ハンドラを呼ぶcontinueの例
script Elizabeth
    property HowManyTimes : 0
    to sayHello to someone
        set HowManyTimes to HowManyTimes + 1
        return "Hello " & someone
    end sayHello
end script
script ChildOfElizabeth
    property parent : Elizabeth
    on sayHello to someone
        if my HowManyTimes > 3 then
            return "No, I'm tired of saying hello."
        else
            continue sayHello to someone
        end if
    end sayHello
end script
tell Elizabeth to sayHello to "Matt"
--result: "Hello Matt", no matter how often the tell is executed
tell ChildOfElizabeth to sayHello to "Bob"
--result: "Hello Bob", the first four times the tell is executed;
--   after the fourth time: "No, I’m tired of saying hello."

スコープ

スコープの区切りはスクリプトオブジェクトとハンドラになります。最上位のスコープをトップレベルと呼びます。暗黙でスクリプト自身はトップレベルのスクリプトオブジェクトです。トップレベル直下のハンドラはスクリプト自身のメンバという位置づけです。

set a to 0

if true then
    set a to 1
end if

get a
--> 1

on foo()
    set a to 2
    get a
    --> 2
end foo

foo()
get a
--> 1

script bar
    set a to 3
    get a
    --> 3
end script

run bar
get a
--> 1

ライフサイクル

トップレベルの変数とプロパティは操作対象のアプリケーションが終了するまで値を保持します。なのでアプリケーションが起動している間にスクリプトを再び実行すると前回の値が残った状態で実行することになります。トップレベルでないスクリプトオブジェクトと変数のライフサイクルはハンドラの終了で寿命を終えます。

try
    log a
on error
    set a to 0
    log "error " & a
end try
set a to a + 1


on foo()
    try
        log a
    on error
        set a to 0
        log "foo ():error " & a
    end try
    set a to a + 1
end foo

foo()


script bar
    try
        log a
    on error
        set a to 0
        log "bar:error " & a
    end try
    set a to a + 1
end script

run bar

ファイル入出力

-- ファイルの書き込み
set filePath to "Macintosh HD:Users:hoge:TheFile"
set fp to open for access file filePath with write permission
try
    write "Some text. And some more text." to fp
on error msg number n
    close access fp
    error msg number n
end try
close access fp

-- ファイルの読み込み
set aFile to "Macintosh HD:Users:hoge:TheFile" as alias
set fp to open for access aFile
try
	set myText to read fp
on error msg number n
	close access fp
	error msg number n
end try
close access fp

-- ファイルの読み込み その2
set myText to read ("Macintosh HD:Users:hoge:TheFile" as alias)

open for access fileでファイルを開きます。with write permissionで書き込み用として開きます。オープンに失敗するとスクリプトは停止します。書き込み時に発生するエラーの対応として開いたファイルを必ず閉じる必要があるのでエラーハンドリングしてファイルを閉じます。

ファイル入出力に関しての説明がコマンドの使い方の説明になっているので書き直す必要あり。また文字コードの扱いに難が有る為、実質的に使えない。

関連リンク

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment