![]() |
ノート/Macメモ/AppleScripthttp://pepper.is.sci.toho-u.ac.jp/pepper/index.php?%A5%CE%A1%BC%A5%C8%2FMac%A5%E1%A5%E2%2FAppleScript |
![]() |
ノート/Macメモ
visitors: 5795 last modified: 2008-08-20 (水) 13:51:35
スクリプト実行環境は、少なくともOS 10.5にはデフォルトで入っているらしい。
スクリプトエディタは、アプリケーションの下のAppleScriptの下にある(スクリプトエディタ.app)。 こいつを起動すればいい。
あちこち見ると、単純なUnix Command-line Applicationは、AppleScriptの "do shell script" で起動するのが簡単らしい。マニュアル:参照
サンプル:
do shell script "cd ~; ls"
をスクリプトエディタで試す。「実行」ボタンを押すと、スクリプトエディタの結果表示ウィンドウに結果が出てくる。
これを、コンパイルしておいて(必要?)、ファイル→保存→フォーマットをアプリケーションにして保存してみる。Finderでfoo.appをダブルクリック。起動するらしいが(画面が一瞬変わる)、何も表示しない。
当たり前だ。スクリプト上で、何も表示することになっていない。
display dialogというのがあるらしい。まずは試してみよう。スクリプトエディタで
set s to do shell script "cd ~; ls" display dialog s
として、無理やり結果をダイアログに表示させてみる。まずはスクリプトエディタの
実行ボタンで確認。次に、ファイルにセーブして、Finder上でダブルクリックで確認。
それらしい結果が出る。
ちなみに、Macで画面キャプチャは、command+shift+4 のあと、マウスで矩形選択、又はspaceを押した後、クリックでウィンドウを指定。デスクトップ?にPNGファイルが作られる。クリップボードにキャプチャしたいときは、control+command+shift+4。
コマンドリファレンス(AppleScript Language Guide: Commands Reference)で見てみる。
サンプルその1 〜 これをスクリプトエディタにコピペして実行したら、OK。 ここで、行末の「?」は、Macのキー「Option+lowercase L」で、継続行を示す。 マニュアル参照。
set prompt to "Please enter password:" repeat 3 times set dialogResult to display dialog prompt ? buttons {"Cancel", "OK"} default button 2 ? default answer "wrong" with icon 1 with hidden answer set thePassword to text returned of dialogResult if thePassword = "magic" then exit repeat end if end repeat if thePassword = "magic" or thePassword = "admin" then display dialog "User entered valid password." end if
サンプルその2 〜 これもスクリプトエディタにコピペして実行したら、OK。 (行末の「?」は、Macのキー「Option+lowercase L」で、継続行を示す。)
set userCanceled to false try set dialogResult to display dialog ? "What is your name?" buttons {"Cancel", "OK"} ? default button "OK" cancel button "Cancel" ? giving up after 15 ? default answer (long user name of (system info)) on error number -128 set userCanceled to true end try if userCanceled then -- statements to execute when user cancels display dialog "User cancelled." else if gave up of dialogResult then -- statements to execute if dialog timed out without an answer display dialog "User timed out." else if button returned of dialogResult is "OK" then set userName to text returned of dialogResult -- statements to process user name display dialog "User name: " & userName end if end
これらのサンプルで分かることは、入力ができること。これで、ラインコマンドの 引数を入力する手段ができることになる。
上記のサンプル1を改造してやってみた。あまり丁寧に考えていない。とにかくできることを証明ればよいことにする。 (行末の「?」は、Macのキー「Option+lowercase L」で、継続行を示す。)
set prompt to "Please enter filename:" set dialogResult to display dialog prompt ? buttons {"Cancel", "OK"} default button 2 ? default answer "wrong" with icon 1 with answer set fname to text returned of dialogResult if fname is "" then display dialog "User entered empty filename" else set s to do shell script "cd ~; ls " & fname display dialog s end if
コマンドスクリプトに入力ファイル名を与えたいことは、とても多いだろう。 専用の仕組が用意されていた。曰く choose file と choose file name である。
choose fileは既存ファイルからの選択メニュー、choose file nameは新しくファイルを作るときに名前をどう付けますかという対話メニューである。
まずは、非常に単純なサンプルを作ってみる。
set fname to choose file with prompt "Enter filename" if fname is "" then display dialog "User entered empty filename" else set s to do shell script "cat " & POSIX path of fname display dialog s with icon 1 end if
ファイル選択画面
選択したファイルに対してdo shell scriptでcatコマンドを実行した結果
この例では選択した後、UNIXのcatで表示しようとしているが、その後に「POSIX path of fname」と しているトリックがある。これは、MacOS上でのファイルオブジェクトを掴むときにaliasを使うのだが、 (表現としては「Hard_Disk:Applications.Mail.app」のようになる)、UNIX (POSIX) 上では POSIX path を使う(「/Applications/Mail.app」)。(マニュアルAliases and Files参照。前者はalias class、後者はPOSIX file pseudo-class になっており、変換が必要になる。上記スクリプトでは、choose fileの出力のファイル名(alias形式)を 「POSIX path of」でPOSIX file形式に変換している。
複数ファイルの同時選択は、choose fileのオプションとして 「with multiple selections allowed」 を付けることによって実現できる。ついでに、promptを日本語文字列にしてみた。
set fname to choose file with prompt ? "複数入力ファイル選択" with multiple selections allowed if length of fname is 0 then display dialog "User entered empty filename" else set c to "" repeat with n from 1 to length of fname set f to POSIX path of (item n of fname) set c to c & " " & f end repeat set s to do shell script "cat " & c display dialog s with icon 1 end if
前と同様、1行目行末の?は鉤の手文字で、継続行を示す。Commandキーを押しながら小文字のl(エル)。
この場合、choose fileの戻り値fnameには、ファイル名(alias)の「リスト」が戻される。
リストについてはマニュアルの「リスト」の項を参照。
5行目からのrepeat文は、文字列cに対して、リストのn番目の要素を(POSIXに変換して)アペンドしている。これは文字列の操作に当たるが、AppleScriptではtextクラスと呼んでいるようだ。同じマニュアルのtextの項を参照。
repeat直後に、do shell scriptで呼び出すcatコマンドの引数としてcのテキストを与えている。つまり、
結果として"cat file1 file2 file3 ..."のようなコマンド文字列をdo shell scriptしていることになる。
ファイル選択画面は
複数選択は、コマンドキー(連番の場合はシフトキー)を押しながらマウスクリック。
実行後のダイアログは
3つのファイルがcatされている。
カレントディレクトリじゃない!!
AppleScriptの中でdo shell scriptをすると、その時点でシェル(説明によると bashらしい)が走り出す。問題は、そのシェルのカレントディレクトリが、 AppleScriptが走る(AppleScriptが置いてある)ディレクトリでは無い点である。
どうして問題か? こういうインストール構図を考えていた。
AppleScriptと、C言語からコンパイルしたバイナリを、必ず同じディレクトリに置く
ようにする(させる)。そうすれば、フォルダ単位でインストールすれば、
お互い(AppleScriptとバイナリ)をどこにおいたかを気にせずに済む。なぜなら、
AppleScriptの中で、「カレントディレクトリにあるバイナリ」と言えばいいから。
(注:別に無理してカレントディレクトリを知らなくても、決めうちのディレクトリに
バイナリを置けばいいじゃないか。全くその通り。
たとえば必ずバイナリを /Applications/ の直下にインストールする、と決めてしまえば、それで解決するのだが、どうも今ひとつ、この決めうちは好きになれない。
移動したら一発でこけるし。)
ところが、そうでなかった。UNIXだとshellからshellを起動すると子のシェルは カレントディレクトリを継承する。ところがAppleScriptでは、do shell scriptが 思い込んでいるカレントディレクトリはルート(/)なのだった。
ググって見ると似たような経験をした人がいるようだった。そこでは、結局
Finderを使ってディレクトリを求める、という方法が提案されていた。
試してみたのが次のような方法。
tell application "Finder" get POSIX path of (folder of (path to frontmost application) as Unicode text) end tell set wd to result --- result はその前のtellの結果 .... set x to do sehll script wd & "fcs20ext" & c --- cには処理対象ファイル名が入っている(fcs20extのコマンドラインパラメタ) display dialog s with icon 1
これで、ファイルとしてセーブしたAppleScriptをダブルクリック起動すると 正しいカレントディレクトリが読める。
これで、fcs20ext.app(AppleScriptをアプリケーションとしてセーブ)と Cをコンパイルしたバイナリfcs20extを同じフォルダ内においてzip、サーバーに アップした。そこからダウンロード・展開して、フォルダ全体を適当なところに置けば インストール完了、という風にした。
さてこの通りインストールして実行してみると(AppleScript Editorは終了しておく)、 なぜか第1回目(インストール後初めて)fcs20ext.appをダブルクリックした時は、 カレントディレクトリが正しくセットされない。2回目からは問題ない。
問題は何か?1つ思いつくのは、第1回目に限りAppleScriptエディタが起動している。 エディタ内から起動すると、エディタがfrontmost applicationになってしまうので、 そのパスが拾われる。
ということで、パスを見つけるターゲットを、frontmost applicationではなくて、 ちゃんと自分自身(「application "fcs20ext"」 、実体はfcs20ext.appらしい) を探して、そこへのパスにすることにする。 "application"はない方がいいのかも?(多分ファイルfcs20ext 〜 Cをコンパイル したバイナリファイル 〜 を探してくれる??)
tell application "Finder" get POSIX path of (folder of (path to application "fcs20ext") as Unicode text) end tell