run script 命令の利用法

AppleScript Programming Tips...と書いたけど、内容はたいしたことはない。[Cocoa Programming Tips](http://hmdt.jp/cocoaProg/index.html "Cocoa Programming Tips | HMDT Programming Tips") の真似がしたかっただけです。知っているとちょっと役に立つかもしれないことをまとめられるといいかな、と。

run script 命令について。load script 命令はわりと話題にでるけど、run script 命令のことってあまり見ない。これは、おそらく run script 命令を使うと処理が遅くなる、ということに起因しているのではないでしょうか。でも、たいして遅くならないよ。今の Mac なら。頻繁に使うとなんだけど、ちょっと使うぐらいなら気にはならない。AppleScript は Mac OS X(特に 10.4 以降)になってからできることが飛躍的に増えて、run script を使うことが個人的には増えました。

run script 命令は文字列(もしくはスクリプトファイルやスクリプトが記述されたテキストファイル)を、実行時にコンパイルし AppleScript として評価する命令です。実際は scripting component(利用するスクリプト言語のこと)を指定することができるのですが、AppleScript 以外を使ったことがないので他の scripting component については割愛します。

run script 命令に渡す文字列は実行時にコンパイルされるので、文法的に正しいなら省略して書いても構いません。

Script Editor で開く

run script "tell app \"finder\" to activate"

文字列をその場でスクリプトとしてコンパイルするので、実行しているスクリプトとは別物のスクリプトが生成されます。ですから、run script で生成したスクリプトは変数やプロパティは共有されません。

Script Editor で開く

set x to 10

run script "set x to 20\r display dialog x as text"

display dialog x as text

このようにそれぞれ個別の変数 x を持っています。ただ、run script 命令はスクリプトの run ハンドラを実行するスクリプトですので、with parameters オプションを使って実行するスクリプトから値を渡すことはできます。

Script Editor で開く

set x to 10

set x to run script "on run argv\nset x to argv\ndisplay dialog x as text\nreturn 20\nend run" with parameters x

display dialog x as text

実際のところ run ハンドラが引数を受け取れるようにしておく状況というのは、

  1. run script から利用するためのスクリプト
  2. シェルスクリプトとして利用するためのスクリプト

のどちらかだと思います。しかし、以下のように常に run ハンドラが値を受け取ることができるようにしておいても何も問題はありません。

Script Editor で開く

on run (argv)
    -- argv は、ハンドラらしく on run (argv) と書いても OK
    -- argv はリストとして解釈される

    if argv is me then
        -- なんらかの処理
        display dialog "me"
    else
        -- run script で値を渡された時の処理
        set argv_class to class of argv
        display dialog argv_class as text
    end if
end run

このスクリプトを Script Editor 等で実行すると display dialog "me" が実行されます。run ハンドラ実行時に渡す引数がないとき、argv は me(そのスクリプト自身)になります。だから、上記のように引数があるときとない時で処理を分岐することができます。

Script Editor で開く

on run (argv)
    -- argv は、ハンドラらしく on run (argv) と書いても OK
    -- argv はリストとして解釈される

    if argv is me then
        -- 通常の処理
        -- このスクリプトを編集、実行しているときはこの if 文が実行される
        display dialog "me"
        -- 自分を呼び出してみる
        -- with parameters に渡すのはリストでもただの文字列でも OK
        run script (path to me) with parameters {10, "100", (current date) as text}
        run script (path to me) with parameters "Hello, World"
        -- with parameters なしで実行すると...予想通り、無限ループ
        -- run script (path to me)
    else
        -- run script で値を渡された時の処理
        set argv_class to class of argv
        display dialog argv_class as text
        repeat with this_item in argv
            display dialog this_item
        end repeat
    end if
end run

このスクリプトを保存してから実行してみてください。run script の動きと渡される引数が何かが分かります。

以前にハンドラを文字列で指定するという話題(文字列で指定したハンドラを実行するその後の「文字列で指定したハンドラを実行する」)を書きましたが、あの時も run script を使いました。スクリプトとして解釈できる文字列を利用する run script 命令は使い方次第でいろいろなことができます。

例えば、System Events の keystroke 命令。この命令は便利なものだけど、ショートカットキーを押すのに修飾キーを指定しないといけない。この修飾キーがあるために汎用的なハンドラにするのは if 文だらけになりそうだ...というとき。以下のように run script を使うときれいにまとまる。

Script Editor で開く

do_keystroke("Finder", "n", "command down") -- 新規 Finder window
do_keystroke("Finder", "n", "{command down, shift down}") -- 新規フォルダ

on do_keystroke(process_name, key_string, modifier_keys)
    -- Send key stroke specify application.
    -- modifier_keys argument is string. "command down" or "{command down, shift down}" ...
    tell application "System Events"
        tell process process_name
            if not frontmost then set frontmost to true
            keystroke key_string using (run script modifier_keys)
        end tell
    end tell
end do_keystroke

例えば、Spaces。Mac OS X 10.5 からは System Events で「システム環境設定」の設定を調べたり、変更したりできるようになりました。Spaces もそのひとつ。以下のスクリプトで Spaces でどのアプリケーションがどの操作スペースに割り当てられているかを調べることができます。

Script Editor で開く

tell application "System Events"
    tell expose preferences
        tell spaces preferences
            application bindings
        end tell
    end tell
end tell

実行してみると分かるのですが、返ってくるのは以下のようなレコード。

{|com.apple.safari|:9, |com.apple.terminal|:65544}

アプリケーションの id がラベルになっていて、操作スペースの数字が値になっている。65544 というのは全ての操作スペースに割り当てた時のものだな。では、上記のようなレコードを作って追加すれば新しいアプリケーションも追加できるのだな。

...と、全くその通りに追加できるのですが、ふと思う。このようなレコードをどうやって作ればいいのだろうか?と。ご存知の通り、AppleScript のレコードは他のプログラム言語にあるようなハッシュや辞書のような使い勝手のいいものではなくて、レコードに新しくラベル(キー)を追加することができない。こういうときに run script。以下のスクリプトでは前面のアプリケーションに操作スペースを割り当てます(スクリプトメニューなどから利用します)。

Script Editor で開く

property upper : "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
property lower : "abcdefghijklmnopqrstuvwxyz"
property all_spaces : 65544

tell application "System Events"
    set front_app to my front_application()
    set identifier to my to_lower(bundle identifier of front_app)
    set app_name to name of front_app

    tell expose preferences
        tell spaces preferences
            set props to properties
            set num to (spaces columns of props) * (spaces rows of props)
            set bindings to application bindings of props
        end tell
    end tell

    set {spaces_list, menu_list} to my make_menu(num)
end tell

tell application (path to frontmost application as text)

    set the_result to choose from list menu_list default items (item 1 of menu_list) with prompt (app_name & " をどこに割り当てますか?")
    if the_result is false then return
    set the_result to the_result as text

    repeat with i from 1 to count menu_list
        if item i of menu_list is the_result then exit repeat
    end repeat
    set target_spaces to item i of spaces_list
    set the_record to my make_record(identifier, target_spaces)
    if bindings contains the_record then
        display dialog app_name & " は " & item i of menu_list & " に既に割り当てられています" with icon 1 buttons {"OK"} default button 1
        return
    else
        display dialog app_name & " を " & item i of menu_list & " に割り当てます" with icon 1
    end if
end tell

tell application "System Events"
    tell expose preferences
        tell spaces preferences
            set application bindings to the_record & bindings
            application bindings
        end tell
    end tell
end tell

on make_record(identifier, spaces_number)
    set r to ("{|" & identifier & "|:" & spaces_number as text) & "}"
    return run script r
end make_record

on front_application()
    tell application "System Events" to return first item of (application processes whose frontmost is true)
end front_application

on make_menu(num)
    set spaces_list to {}
    set menu_list to {}

    repeat with i from 1 to num
        set end of spaces_list to i
        set end of menu_list to "Spaces " & (i as text)
    end repeat

    set end of spaces_list to all_spaces
    set end of menu_list to "All Spaces"
    return {spaces_list, menu_list}
end make_menu

on to_lower(the_text)
    set char_list to characters of the_text

    considering case
        repeat with char in char_list
            set num to offset of char in upper
            if num is not 0 then set contents of char to character num of lower
        end repeat
    end considering

    return char_list as text
end to_lower

これは単純なサンプルですが、Spaces のように簡単に追加や削除がやりにくいレコードで結果を返すアプリケーションって、増えているのです。格好のいいものではないのですが、レコードの動的生成はよく使います。あと run script でよく利用するのは、先ほども書いたハンドラを文字列で指定するとき。単純にハンドラを文字列で指定するだけなら以下のような方法で十分でしょう。

Script Editor で開く

on run
    set my_path to path to me as text
    set the_method to run script ("say_hello of (load script file \"" & my_path & "\")")

    the_method("Bob")
end run

on say_hello(your_name)
    display dialog ("Hello, " & your_name)
end say_hello

保存されていることが条件ですが、これもよく利用します。

0 件のコメント :

コメントを投稿