ホーム > HTMLに役立つヒント>AppleScriptfor
OS X
あなたはここへ来た、
63886番目の人ですです。
(2005/4/26から)
Macintosh OSXのアップルスクリプトについての覚え書き。自分用なので説明が足りなかったり、順番バラバラだったり、あまり参考にならないかも。間違った記述があ るかもしれません。特に断わりがない限り、このページ内ではXcodeを使ったAppleScriptの開発のことです。
ページ内にスクリプトがいくつか書いてありますが、ご利用はご自由にどうぞ。ただし、これらを使って何らかの損害を被ってもいっさいの補償はあり ません。アンドなんかバグってるかも。オウンリスクでね。
いやー、しかしOS XではAppleScriptに簡単にインターフェースがつけられたり、便利な世の中になりましたな。これで処理速度がもう少し……それは言わない約束 よ。
最初に「コマンド+N」で「アシスタント 新規プロジェクト」を表示させ、これから作るアプリケーションを選ぶことになる。
AppleScript関連は三つもあり、どれを使ってよいやら、というのが第一関門。
・AppleScript Application
インターフェースなんかをつけたいならこれを選ぶといいでしょう。
・AppleScript Document-based Application
ターミナルなどからコマンドを打ち込んで呼び出す、UNIX上で動作するスクリプトを作る時使う。でも、このタイプはPerlで書いた方がいいん じゃ……。
・AppleScript Droplet
ドロップレットやアップルスクリプトアプリケーションなど、従来タイプのインターフェースのないスクリプトならこれ。
旧MAC OSのアップルスクリプトでは「on run〜end run」というrunハンドラの間に書いたスクリプトが起動時に実行される仕組みだった。
これが、「on launched theObject 〜 end launched」というlaunchedハンドラの間に書くように変わっている。
ドロップレットの場合は「on open 任意の変数名 〜 end open」でかわっていない。
また、1行が長くなったら半角かなの「ツ」(シフト+リターンで入力)で、みかけは複数行だが1行扱いだった。旧スクリプトをそのまま持ってきた 時、これでエラーがおこる。Xcodeでは「ツ」(シフト+リターンで入力)があってはいけない。この手の改行は削除する必要あり。
これまではわざわざquitを記述しなくても、処理が全部終わったら勝手に終わってくれていた。Xcodeではquitをちゃんと記述しないとい けないようだ。
これらの点だけ心得ておけば、後はこれまでのスクリプトと同じように書ける。らしい。
「AppleScriptStudio でゼンマイびゅんびゅん」を読んでいたら、propertyの扱いがこれまでと違っていることが書いてあった(P103/P117)。
これまではpropertyは前回の変数の内容を覚えておいてくれる便利なものだった。ところが、OS Xでは前回の内容を保管できないのだった。
ではpropertyとglobalの違いなんなのか、ということになる。propertyは初期値を指定できるが、globalはできないとい う点だけである。
OS X的にはuser defaultsやdefault entryを使ってユーザーディレクトリのLibrary/Preferences/ファイル名.plistに書き込んだり読み込んだりするのが正しい。 何しろそのための仕組みが用意されているんだから。
しかし、アプリケーションごとに前回の値を覚えておきたい場合もあるのさ。というわけでパッケージ(アプリ名.app)内にpropertyファ イルを持つことにする。で、その読み出しルーチン。必要なpropertyの文だけファイルを作っておけばいいでしょう。まだためしてないけど。
on getProperty(propertyName)
set thisFldr to resource path of main bundle as string
--パッケージ内のResourcesまでのパスが入る
--その中のpropertyNameで受け取った名前のファイルを読み込む
set thisFldr to thisFldr & "/" & propertyName
read thisFldr --書き込む場合はwrite 内容 to ファイル名
set kekka to result --読み込んだ内容を変数にセット
end getProperty
OS XのProject Builder/Xcodeで使うための自作のAppleScriptの関数(サブルーチン)いくつか。再利用しそうなのでメモ。
--初期設定ファイルに保存する
--savePlist(項目名,内容)のように呼び出す
on savePlist(thisKey, thisContent)
try
--初期設定ファイルに保存する
set contents of default entry thisKey of user defaults to thisContent
on error --ない場合は作る
make new default entry at end of default entries of user defaultswith
properties {name:thisKey, contents:""}
set contents of default entry thisKey of user defaults to thisContent
end try
end savePlist
--初期設定ファイルから読み込む
--set aaa to loadPlist(key) のように呼び出す
on loadPlist(thisKey)
try
--初期設定ファイルから読み込み
set thisCont to contents of default entry thisKey of user defaults
on error --初期設定ファイルがない/項目がない場合
set thisCont to ""
end try
return thisCont --読み込んだ値を返す
end loadPlist
--ウインドウ位置を保存
on saveWinPos()
set loc1 to position of window "win1"
set content of default entry "xLoc" of user defaultsto
item 1 of loc1
set content of default entry "yLoc" of user defaultsto
item 2 of loc1
--display dialog "X:" & item 1 of loc1 & " y:"&
item 2 of loc1
set rec1 to bounds of window "win1"
set content of default entry "xLoc2" of user defaultsto
item 3 of rec1
set content of default entry "yLoc2" of user defaultsto
item 4 of rec1
--set content of default entry "winWidth" of user defaultsto
((item 3 of rec1) - (item 1 of rec1))
end saveWinPos
--ウインドウ位置を読み込んで位置を動かす
on setWinPos()
try
set x1 to contents of default entry "xLoc" of user defaults
set y1 to contents of default entry "yLoc" of user defaults
set x2 to contents of default entry "xLoc2" of userdefaults
set y2 to contents of default entry "yLoc2" of userdefaults
set bounds of window "win1" to {x1, y1, x2, y2}
on error
make new default entry at end of default entries of user defaultswith
properties {name:"xLoc", contents:0}
make new default entry at end of default entries of user defaultswith
properties {name:"yLoc", contents:0}
make new default entry at end of default entries of user defaultswith
properties {name:"xLoc2", contents:100}
make new default entry at end of default entries of user defaultswith
properties {name:"yLoc2", contents:100}
end try
end setWinPos
set thisFldr to path to me --MacOS版と同じやり方
OS Xではアプリケーションの正体は「xxx.app」というフォルダです(そうでない場合もありますが)。普通「.app」の拡張子は表示されず、フォルダ のくせにアプリとしてふるまいます。で、こんな方法もあります。次項のPerlスクリプトとの連係で便利。
set thisFldr to resource path of main bundle as string --パッケージ内のResourcesまでのパスが入る
OS XはUNIXです。最初からちゃんとPerl(10.2まではperl5.6、10.3ではperl5.8.1)も入ってます。Perlは文字の処理とか ファイル操作とかが得意です。例えばファイルを読み込んで処理をするような場合や、フォルダやファイルそのものを操作(移動したり削除したり)する場合、 AppleScriptでFinderを制御して仕事をやらせるより、Perlに処理させた方がはるかに処理スピードは速いです。測ったことはないです が、確実に10倍以上は速いでしょう。ほーら、AppleScriptにインターフェースを任せて、Perlで処理をしたくなって来たでしょう(^_^)
できるんですね、これが。「do shell script命令」という、シェルに命令を送るスクリプトが書けるんです。これを使って、「do shell script "perl/aaa/bbb/xxx.pl"」とやってやれば自作のPerlスクリプトが動きます。
●do shell scriptミニ知識 本来これはAppleScriptからシェルコマンド(デフォルトではsh)を実行するための命令。構文としては「do shell script シェルコマンド」となる。管理者権限が必要な場合は次のようにする。 do shell script シェルコマンド password "管理者パスワード" with administrator privileges --ダイアログなしで実行できる。 do shell scriptの解説はここらへん。 |
テストしてみたところ、ちゃんとPerlスクリプトに引数も渡すことができます。
do shell script "
perl /aaa/bbb/xxx.pl 引数1 引数2" --AppleScriptからPerlスクリプトに引数を渡す。
これを受けるPerlスクリプト側では$ARGVで受け取れます。
#!/usr/bin/perl
$insuu1 = $ARGV[0] ; #--第一引数
$insuu2 = $ARGV[1] ; #--第二引数
print "$insuu1\n";
print "$insuu2\n";
exit;
このスクリプトは引数を2つ受け取って、そのままプリントするだけのものです。Perl側でprintすると、それがAppleScript側に 返されます。これをAppleScriptで受け取るには、こんなふうにします。
set kekka to result --結果を受け取る。最初からset kekka to do shell script 〜という書き方でもOK
前述のdo shell〜の次にこう書いておけば、Perlスクリプトの返してくる結果はkekka(任意の変数名)に入ります。あとは表示するなりなんなりできま す。
Perlスクリプト側はSJISで書いてもEUCで書いても文字化けせずに返事を返してくれるようです。UTF-8で書くと改行がおかしくなり、 unicodeで書くとAppleScriptエラーが出ますので御注意。Perlのお作法に従ってEUCで書くのがいいでしょう。
あ、もちろん改行コードの方はLFです。UNIX perlですから。
それから、引数を渡す時、sjisで書かれたPerlスクリプトでよく問題になる「表示」といったような文字を渡そうとするとエラーが出ます。 「表\示」とするとすんなり引き渡されます。次項で扱うドロップされたリストをPerlに渡す場合はこうしたエラーは出ません。直接引数を書いた場合の話 です。
おまけ。Perlな人はすぐ分かると思いますが、引数がいくつあっても次のようにすればOK。
#!/usr/bin/perl
foreach $thishikisuu(@ARGV){
# ここに処理を書けばよい
print "$thishikisuu\n";
}
exit;
こうなると逆パターン、PerlからAppleScriptを呼び出したくなるのが人情。
が、今のところ僕には分かりません。あんまり探っていないので。ひょっとして、「新規プロジェクト」で 言及した「AppleScriptDocument-basedApplication」としてAppleScriptを作り、これを受け皿とすれば(こ のタイプはターミナルから呼び出せるので)実現できる可能性はあります。ためしていないのでなんとも言えません。ご存知でしたら教えて下さい。
旧Mac OSで動く(OS XでもClassic環境で動作する)MacPerlが便利だったのは、簡単にドロップレットが作れるから。何しろスクリプトをドロップレットとして保存 するだけですから。本家Perlにはまねできません。
んが、これまで述べて来たように、AppleScriptからPerlを呼び出せますから、これを利用してPerlドロップレットを作ることも可 能です。ドロップを受け取る部分をAppleScriptが担当し、処理をPerlが受け持てばいいわけです。作るのはMacPerlほど簡単じゃないで すけど。
Xcode(やっと慣れて来たと思ったら1.5にアップしてAppleScriptの作成手順が微妙にかわった。くそ)を使って、 AppleScriptでドロップレットを作り、Perlスクリプトを同梱(XcodeでProject→AddtoProjectメ ニューを選択し、プロジェクトにPerlスクリプトを取り込む)しなくてはいけません。めんどい。
こうして同梱したPerlスクリプトは、ビルドするとパッケージの「Contents/Resources」直下に入ります。「自分のパスを取得する」の手法で、パッケージがどこにあってもPerlスクリプトのパスは取りだせますので、ドロッ プされたものを引数としてPerlスクリプトに投げてやればいいです。
ただし、ドロップされたものをPerlに渡す前に、UNIX式のパスの記述に変換するのを忘れないように。「POSIX pathof〜」でOK。
on open names --OS X式ドロップレットハンドル
--パスを取得する
set thisFldr to resource path of main bundle as string
--パッケージ内のResourcesまでのパスが入る
set myScript to thisFldr & "/collPlTest.pl" --スクリプト名を追加
repeat with thisObj in names
set thisObjPath to POSIX path of thisObj --UNIX式のパスに変換
set kekka to do shell script "perl " & myScript&
" " & thisObjPath
display dialog kekka
end repeat
quit
end open
なんて具合に一つずつPerlに渡していくとか。
一気に渡すなら、namesを半角スペース区切りのテキストに変換してから渡してやればよい。一気にPerlに渡した方がAppleScript を介さない分、処理は高速。フツーはこちらがお勧め。
on open names --OS X式ドロップレットハンドル
--パスを取得する
set thisFldr to resource path of main bundle as string
--パッケージ内のResourcesまでのパスが入る
set myScript to thisFldr & "/collPlTest.pl" --スクリプト名を追加
set thisObjPath to "" --初期化
repeat with thisObj in names --リストを取り出して半角区切りのテキストにする
set thisObj to POSIX path of thisObj --UNIX式のパスに変換
set thisObjPath to thisObjPath & space & thisObj
--半角スペースをつけてつなげていく
end repeat
set kekka to do shell script "perl " & myScript&
" " & thisObjPath --引数としてリストを一気に渡す
display dialog kekka
quit
end open
受け取るPerlスクリプト側は「AppleScriptとPerlの連係」を参照のこと。こう して受け渡されたパスは、日本語が含まれていてもPerl側では何も処理する必要はありません。ちゃんとディレクトリやファイルをオープンできます。
ま、こんな感じでMacPerlのドロップレット資産もOS Xでちゃんと生かせます。といいつつ、書き換えるのが面倒なのですでに作っちゃったMacPerlのドロップレットはClassic環境でそのまま使って たりして(^_^;
AppleScriptでPerlスクリプトをドロップレット化した例として「Mozilla_Correct」を紹介して おきます。フリーウエアですし、改造も自由なので、Perlドロップレットのたたき台として使ってやってください。
2008/4/16追記 「Perl ドロップレット/アプリひな形」を作りまし
た。Perlドロップレットのたたき台にしてください。
Perlの話が出たついでにちょっと横道。
OSXのHFS+システムがUTF-8でファイル管理しているのは有名ですが、実は通常使われている正規化NormalizationFormハ Cではなく、NormalizationFormハD(NFD)を使ってます。FormCと違うところは、例えば「ガ」という文字は「カ゛」と2文字とし て扱われます。「パ」なども同様。
参考
●http://developer.apple.com/ja/technotes/tn1150.html#UnicodeSubtleties アップル自身の説明
●http://homepage1.nifty.com/glass/tom_neko/web/web_webdav.html#mod_encoding とむねこさんの分かりやすい解説。
Perlがファイル/ディレクトリ名を取得する場合はこのことを考える必要はないです。ちゃんと内部で解決してくれる模様。
opendir (DIR , $pathB) || die "Open Error ! Cannot opendir
$pathB" ;
@filelist = readdir(DIR);
closedir(DIR) ;
foreach $thisitemB (@filelist) {
print "$thisitemB\n";
}
などとして「$pathB」で指定したフォルダの中のリストを作ると、ちゃんと日本語のファイル/フォルダ名も取得されています。ただし、UTF -8(NFD)で取得されているので、そのことは忘れないように。
上でAppleScriptからパスが渡された場合をあつかってますが、これも大丈夫。AppleScript側でPOSIXを使ってUNIX形 式のパスにさえ変換しておけば、パスに日本語が含まれていても問題ありません。
さて、問題となるのは、なんらかの理由で「$pathB = "/aaa/bbb/ここなんだよ/test/";」などのように直接指定した場合。このままではディレクトリをオープンできません。
Perl 5.8.1なんだからEncodeモジュール使えばいいジャン、MacJapaneseもサポートしているんだからさぁというあなた、おしい。
参考
●http://perl.active-venture.com/pod/perljp.html Encode がサポートしているコード
use Encode (from_to);
from_to($pathB, 'shift-jis', 'MacJapanese');
としてもうまくいかないんですよ。utf8でもうまくいかないし。結論を言うと、その前にUnicode::Normalizeを使ってNFDに してやる必要があるんです。はまりました、これ。で、こんなふうにします。
use Encode (from_to); # utf-8に変換するモジュールの宣言
use Unicode::Normalize; # NFD(Macintosh特有のUTF)に使うモジュールの宣言
$pathB = NFD($pathB); # 渡されたパスをNFDに
from_to($pathB, 'shift-jis', 'utf8'); # UTF-8に
NFD($pathB)ですでにUTFになっているのではと思ったら違うんですよね、なぜか。次のfrom_to($pathB,'shift- jis','utf8')ではshift-jisとして指定してutf-8に変換してやらないとうまくいかない。理由はよくわかんないです。両方のモ ジュールの動きをよく理解していないもので。まー、とにかくこういう処理をすれば使える、ということで。
ドロップレットを作っていて、ドロップされたファイルのうち、特定の拡張子のものだけ処理したかった。このとき、旧MacOSの AppleScriptとちょっと違うところがあって、判定に苦労してしまった。
ドロップしたアイテムから名前を取り出す際、このように書かなくては次の拡張子の判定で失敗するのだ。
set myFile to name of item obj ← objというのがドロップされたアイテム。ここで大切なのが「item」である。「setmyFileto name ofobj」でもエラーはでないが、取り出しがうまくいっておらず、拡張子の判定でおかしくなる。
拡張子の判定部分はこんな感じ。
if (".html" is in myFile) or (".htm" isinmyFile) or (".shtml" is in myFile) then ← よーするにhtml/htm/shtmlファイルだけを処理対象にしたいのね。
ちょっとしたことなんだけで、延々つまずいてしまったのでメモしておくのだ。
AppleScriptで日本語のメッセージを出すと文字化けしてしまう。これを回避するには次の手順を行う。
1.保存時に「アプリケーションバンドル」で保存する。
2.保存したパッケージを開く(Finderでコントロール+クリックして「パッケージの内容を表示」を選択)
3.Contents/info.plistを編集する。
編集する箇所は1箇所。「<string>English</string>」を「<string> Japanese</string>」にする。
以上で終わり。
さてさて、perlと連動してドロップレットを作ったような場合、perlから単純にAppleScriptに結果を返すと、そのままでは文字化 けする。perlは一般にEUC-JPで動いているのにAppleScriptはShift_jisだからだ(たぶん)。
少なくとも10.3.9ではperl5.8が動いているので、Encode モジュールが標準で入っている。これを利用して一工夫する。
use Encode (from_to); # perlスクリプトの最初の方に書く。
$msg = "日本語が化けちゃヤダ" ;
from_to($msg, 'euc-jp', 'shiftjis'); # shiftjisに変換
print "$msg";
これでAppleScript側で文字化けせずに済む。なお、現在のお作法としてはperlスクリプトはunicodeで書くのが正しいらしい。 あたしゃ従来通りEUC/LFで書いてますが。
インターネットの動画ニュースを見ていたりすると、デスクトップにどんどん.aswファイルや.ramファイルがたまっていく。尼崎の列車脱線事 故のニュースを見たくてたどっていったら、デスクトップがえらいことになった。一発でこれらをゴミ箱に放り込みたいと思ったので書いてみた。OSX版で動 作している。
| --デスクトップの.ram/.aswファイルをゴミ箱へ入れる tell application "Finder" delete (every item of desktop that (".ram" is in name) or (".asw" is in name)) end tell |
結構わかりにくいので、別ページにして解説しています。「アイコンの作り方 for Xcode」をどうぞ。