AppleScript Programming Tips (5)

AppleScript Programming Tips (4) の続きを受けて、ユーザーが定義した命令(handler、ハンドラ)について書こうかと思いましたが、先に「オブジェクトの指定」について書こうと思います。オブジェクトを指定しないことには命令も送れないことですし。なんだか、順番が無茶苦茶だなとは思いますが...。

この文章は AppleScript Language Guide を基にしています。おかしな表現や間違い、文責は私にあります。

object specifier...なんて訳せばいいのでしょう。とりあえず、「object specifier」、もしくは「オブジェクトの指定」でいきます。AppleScript でどのようにオブジェクトを指定するのか?また、指定したオブジェクトを AppleScript がどのようにして認識するか?といったことについての話です。

オブジェクトの指定ということですが、オブジェクトの参照形式(reference form)とは違い、オブジェクトの指定は「オブジェクトの種類 + オブジェクトの場所 + オブジェクトの参照形式」の組み合わせからなります。

まず、基本的な事として AppleScript のオブジェクトは、たいていの場合他のオブジェクトに内包されています。オブジェクトを内包しているオブジェクトの事をコンテナ(container)といいます。例えば、file は folder に内包されていますし、folder は disk に内包されています。また、character や word、paragraph は text に内包されています。

object specifier は処理対象オブジェクトを同定するのに必要な情報を明示することです。

object specifier はスクリプトの実行時に評価(解決)されます。スクリプトのコンパイル時ではありません。例えば、以下のスクリプトはコンパイルはできますが、実行時にはエラーになります。

window 10000 of application "Finder"

また、アプリケーションは命令の結果として object specifier を返します。

Script Editor で開く

window 1 of application "Finder"
--> Finder window id 858 of application "Finder"

オブジェクトのトップレベルのコンテナ(top-level container)はアプリケーションです。AppleScript がオブジェクトの指定を解決する時、命令の時と同じようにオブジェクトの継承関係をたどり、どのオブジェクトが指定されているのかを評価します。

object specifier はオブジェクトの種類(クラス)、オブジェクトの場所(オブジェクトを含むコンテナ)、そして、オブジェクトを識別する方法(参照形式、reference form)から成り立っています。

Script Editor で開く

tell application "Script Editor"
    paragraph 1 of document 1
end tell

このスクリプトはまず、オブジェクトの種類(paragraph)を指定しています。次にオブジェクトを含むコンテナ(document 1)を指定しています。が、tell でアプリケーションが指定されています。トップレベルのコンテナはアプリケーションですので正確にはアプリケーション Script Editor の document 1 がコンテナになります。そして、オブジェクトを識別する方法がクラスとインデックス参照の組み合わせになっています(paragraph 1)。

これらが評価されて「アプリケーション Script Editor の最前面のドキュメントの 1 行目」が指定されることになります。

AppleScript のオブジェクトはコンテナ(container)に含まれているものですが、コンテナもまたオブジェクトです。コンテナは 1 つ、または複数のオブジェクトや属性を持っています。目的のオブジェクトを探すにはコンテナを指定します。コンテナは of(または、in)で指定します。

例えば、Finder で Applications フォルダを指定するにはコンテナ(startup disk)を指定し、folder クラスを名前参照形式と組み合わせます(もちろん、他にも方法はあります)。

Script Editor で開く

tell application "Finder"
    folder "Applications" of startup disk
    folder "Applications" in startup disk -- 上の行と同じ意味
end tell

一連のコンテナを指定して目的のオブジェクトを探すにはコンテナの一番深い部分から指定します。Application フォルダ内の Safari.app を指定したいなら以下のようになります。

Script Editor で開く

tell application "Finder"
    application file "Safari" of folder "Applications" of startup disk
    (*
    application file "Safari" 
        - クラスの種類と名前参照形式の組み合わせ
    ~ of folder "Applications" of startup disk
        - コンテナの指定
          コンテナは一番深いコンテナから指定し、上の階層を of(in) でつなぐ
    *)
end tell

上記の of を使った指定は一番最初に「目的のオブジェクト」が記述され、そのあとに「コンテナ」、「コンテナ」...と続きます。

所有格(英語の「's」)をコンテナに用いることで一番深いコンテナから記述することもできます。

Script Editor で開く

tell application "Finder"
    startup disk's first folder's first file
    startup disk's folder 1's file 1 -- こちらでも可
end tell

どちらがいいかは好みの問題です(個人的には同じことを様々な書き方で指定できることには疑問を覚えます。AppleScript の初心者がつまずきやすい部分だと思います)。

オブジェクトの参照形式(reference forms)についても触れておきます。AppleScript の参照形式は特定のオブジェクトを識別するためのもので、object specifier とともに使われます。参照形式には以下のものがあります。

  1. 任意参照
  2. 全要素参照
  3. フィルタ参照
  4. ID 参照
  5. インデックス参照
  6. 中央参照
  7. 名前参照
  8. 属性参照
  9. 範囲参照
  10. 相対参照

任意参照形式は複数のオブジェクトの中から任意のオブジェクトを参照します。

Syntax - Reference form Some例文の記述法について

some class

Parameters

class
任意のクラス識別子。

Script Editor で開く

tell application "Finder"
    some file of folder "Applications" of startup disk
end tell

このスクリプトは実行の度に結果が変わると思います。任意参照は複数のオブジェクトの中からランダムにオブジェクトを指定する参照形式です。StandardAdditions.osax にランダムな数値を返す random number 命令がありますが、AppleScript を使う人たちの間ではいまいち人気がない。なぜかというと、スクリプティングアディションの呼び出しには時間がかかるから。頻繁にスクリプティングアディションの命令を呼び出すなら、代替方法を探す...というのは AppleScript 高速化 Tips のひとつですが...今もやっぱり有効なのかな?

全要素参照形式は、コンテナの中から指定したクラスのオブジェクトを全て参照します。

Syntax - Reference form Every例文の記述法について

every class

Parameters

class
任意の class。もしくは class の複数形。

構文は some と同じように every の後にクラスを指定し、結果は指定したクラス(のオブジェクト)を含んだリストです。指定したクラスがなかったら空のリストを返します。

Script Editor で開く

tell application "Finder"
    every file of folder "Applications" of startup disk
end tell

AppleScript では頻繁に使う参照形式です。一括処理のときにお世話になります。全要素参照形式は every を使わず指定するクラスを複数形にする事もできます。

Script Editor で開く

tell application "Finder"
    files of folder "Applications" of startup disk
end tell

全要素参照形式を使うとリストの複製もできます(あまり使わないですが)。

Script Editor で開く

set the_list to {1, 2, 3}
set new_list to items of the_list

set item 1 of the_list to "A"
{the_list, new_list}

フィルタ参照形式は、指定したコンテナの中から条件にあったオブジェクトを参照します。

Syntax - Reference form Filter例文の記述法について

object_specifier (whose | where | that) boolean_test

Parameters

object_specifier
対象となるコンテナ内のオブジェクト。
boolean_test
真偽値を返す式。

Result

リストの中から boolean_test を通ったオブジェクトのリスト。 boolean_test を通るオブジェクトがない場合、空のリストが返る。

条件式は真偽値を返すものになります。whosewherethat はどれを使っても同じです。返ってくる結果は条件に合致したオブジェクトのリストになります。

Script Editor で開く

tell application "Finder"
    files of folder "Applications" of startup disk that ¬
        name starts with "A" -- 名前が「A」で始まる全てのファイル
end tell

このように全要素から条件式で一致するものだけを抽出します。強力なフィルタ参照なのですが、AppleScript のオブジェクト(リスト、レコード、テキスト...)には利用できません。利用できるのはアプリケーションのオブジェクトに対してだけです。また、アプリケーション側でも対応している必要があります。

Script Editor で開く

-- Finder でズームしているウィンドウを戻す
tell application "Finder"
    set zoomed of (windows whose zoomed is true) to false
end tell

このようにオブジェクトの属性を変更するときにも使えます。また、コンテナの指定を後置きにしてフィルタ参照の部分を簡潔に記述する事ができます。

Script Editor で開く

tell application "Finder"
    -- フィルタ参照を () で囲み、in でコンテナを指定
    (files whose name extension is "scpt") in desktop
end tell

フィルタ参照形式は基本的に以下のような動きで処理を行っています。

  1. 対象となるコンテナ内の指定されたクラスのオブジェクトを全て取得する
  2. 上記の結果がリストとして返ってくる
  3. このリストの中からオブジェクトをひとつずつ取り出し、条件に照らし合わせる
  4. 全てのオブジェクトについて照合が終わる
  5. 条件に合致したオブジェクトをリストとして返す

例文として紹介しているスクリプトは省略した書き方です。省略部分を記述すると上記の流れが分かりやすくなります。

Script Editor で開く

tell application "Finder"
    files of desktop whose (name extension of it) is "scpt"
    (*
    - files of desktop を取得
    - 結果のリストからひとつずつ取り出す
    - it(記述を省略していた部分)が取り出されているオブジェクト
    - 「name extension of it」は、 desktop の「ファイル某」の拡張子を取得している
    - name extension of it is "scpt" が false のものは除去
    - フィルタリングされた結果が返る
*)
end tell

「it」を省略していたのですが、あるとスクリプトが多少分かりやすくなります。

ID 参照形式は、オブジェクトの ID でオブジェクト参照します。最初にオブジェクトの ID の値が分かっていないと使えないもの参照形式でもあります。

Syntax - Reference form ID例文の記述法について

class id expression

Parameters

class
任意のクラス識別子。
expression
オブジェクトの id を表す値。

Script Editor で開く

tell application "Finder"
    disk id -100
    --> startup disk of application "Finder"
    name of disk id -100
    --> "Macintosh HD"
end tell

AppleScript 2.0 以降なら ID 参照形式でアプリケーションと文字列を参照することができます。ID は数値で表される事が多いのですが、実際は決まっていません。アプリケーションオブジェクトは固有の ID を持っていますが、それは文字列です。

Script Editor で開く

-- アプリケーション ID でアプリケーションを指定
if not (running of application id "com.apple.Mail") then
    activate application id "com.apple.Mail"
end if

-- 文字列を id で取得
set the_text to "Hello, World"
set text_ids to id of the_text
--> {72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100}

--id からテキストを取得
string id text_ids
--> "Hello, World"

インデックス参照形式はコンテナ内の要素を数値で指定する参照形式です。

Syntax - Reference form Index例文の記述法について

class integer
integer (st | nd | rd | th) class
(first | second | third | fourth | fifth | sixth | seventh | eighth | ninth | tenth) class
(last | front | back) class

Parameters

class
任意のクラス識別子。
integer
数値。
st | nd | rd | th
1st、2nd、3rd...といった指定を行いたい時の用語。

Script Editor で開く

set the_list to {"A", "B", "C"}

item 1 of the_list
--> "A"

tell application "Finder"
    file 1 of desktop
end tell

set the_text to "Hello, World"
word 1 of the_text
--> "Hello"
word -1 of the_text
--> "World"

インデックス参照形式は数値にマイナス(-)をつける事で要素の最後から取得する事ができます。インデックス参照形式は絶対的な参照ではなく、相対的な参照です。状況により結果は変わります。

中央参照形式はコンテナの中から中央のオブジェクトを参照します。

Syntax - Reference form Middle例文の記述法について

middle class

Parameters

class
任意のクラス識別子。

正直、使っているとこを見た事もないし、どういったときに使うといいのか分からないのですが...。

Script Editor で開く

tell application "Finder"
    middle file of desktop
end tell

名前参照形式は名前でオブジェクトを参照します。

Syntax - Reference from Name例文の記述法について

class name_text

Parameters

class
任意のクラス識別子。
name_text
オブジェクトの名前属性の値。

オブジェクトの名前というのはファイルならファイル名、ウィンドウならウィンドウのタイトル、フォルダならフォルダ名...といったようにそのオブジェクトが持っている固有(Finder 等が表示している)の名前の事です。

Script Editor で開く

tell application "Finder"
    window "書類"
end tell

絶対参照になるので例えば上記のスクリプトなら「書類」というタイトルのウィンドウがない場合、エラーになります。

属性参照形式はオブジェクトの属性を取得する時に使う参照形式です。

Syntax - Reference form Property例文の記述法について

property_label

Parameters

property_label
オブジェクトが持っているプロパティ(属性)。

Script Editor で開く

tell application "Finder"
    zoomed of window 1
    --> false
end tell

オブジェクトがどのような属性を持っているかはアプリケーションの「用語説明」を見て確認します。

Script Editor で開く

tell application "Script Editor"
    properties of word 1 of paragraph 2 of front document
    --> {class:text, color:{0, 0, 65535}, size:12.0, font:"Courier"}
end tell

オブジェクトの全ての属性を取得するには properties を使います。しかし、properties は AppleScript のオブジェクトには使えません。

範囲参照形式はコンテナの中から範囲を指定してオブジェクトを参照形式します。

Sytax - Reference form Range例文の記述法について

class start_index thru end_index of object_specifier

Parameters

class
任意のクラス識別子。
start_index
要素の始まりの位置を表す整数値。
end_index
要素の終わりの位置を表す整数値。
object_specifier
コンテナオブジェクト

記述はいろいろありますが、構文確認時に上記の形式に変換されるのでこれを覚えておくといいと思います。

Script Editor で開く

set int_list to {101, 31, 49, 98, 56, 71}
integers 2 thru 4 of int_list
--> {31, 49, 98}

この範囲指定をよく使うのは文字列から部分文字列を取り出す時でしょう。

Script Editor で開く

tell application "Finder"
    set the_file to name of file 1 of desktop
    --> "Active.app"
    set num to offset of "." in the_file
    text 1 thru (num - 1) of the_file
    --> "Active"
end tell

相対参照形式は、あるオブジェクトからの相対でオブジェクトを参照します。

Syntax - Reference form Relative例文の記述法について

[class] (before | [in] front of) base_specifier
[class] (after | [in] back of | behind) base_specifier

Parameters

class
任意のクラス識別子。
base_specifier
基準となるオブジェクト。

Script Editor で開く

tell application "Finder"
    file before file 3 of desktop
    file in front of file 3 of desktop
end tell

あまり使う事がないのですが...(というか、使う事のない参照形式の方が多いのですが)。

AppleScript では以上のような参照形式を使って特定のオブジェクトを指定します。

最後に オブジェクトの指定と reference オブジェクトについて。reference オブジェクトというのはいわゆる「ポインタ」で、object specifier を reference オブジェクトでラップしたものです。

reference オブジェクトは a reference to 演算子で作成します。

Script Editor で開く

on run
    set file_list to desktop_files()
    --> every file of desktop of application "Finder"

    tell application "Finder"
        -- 利用したいときに中身を取り出す
        file_list as text
        name of file_list
        set file_list to contents of file_list
        repeat with this_file in file_list
            contents of this_file
        end repeat
    end tell
end run

on desktop_files()
    tell application "Finder" to a reference to files of desktop
end desktop_files

reference オブジェクトは実際に利用したいときに中身を取り出してから利用するので、その時までの動作を軽くする/速くする効果があります。ただ、オブジェクト指定やオブジェクトの参照を保持しているだけなので実行時に対象オブジェクトが変わる事があります。以下のスクリプトでは iTunes で同じ曲名が表示されると思います。

Script Editor で開く

tell application "iTunes"
    activate
    set current_track to current track
    display dialog (get name of current_track)
    --> "Head Down"
    next track
    display dialog (get name of current_track)
    --> "Head Down"
end tell

しかし、reference オブジェクトを使うと正しい曲名が表示されます。

Script Editor で開く

tell application "iTunes"
    activate
    set current_track to a reference to current track
    display dialog (get name of current_track)
    next track
    display dialog (get name of current_track)
end tell

reference オブジェクトから中身を取り出すには reference オブジェクトの属性 contents を利用します。

Script Editor で開く

tell application "Finder"
    set window_ref to a reference to Finder window 1
    -- Finder window 1 of application "Finder"
    -- object specifier

    contents of window_ref
    -- Finder window id 460 of application "Finder"
    -- object specifier を評価した結果

    get window_ref
    -- Finder window 1 of application "Finder"
    -- object specifier
end tell

get 命令では object specifier を評価しません(reference オブジェクトが得られる)。