AppleScriptからdo script を使ってコマンドラインを呼び出すとき、スペースが入ったパスが含まれるとエラーになる問題があります。これを解決する方法。ついでに do script にどのようにコードを填め込むかの回答に至ります。
はじめに
本文を前にこんなこと言うのも何ですが、ここの内容を簡潔にまとめた上にさらなる知恵を得られる記事が後に公開されましたのでご案内しておきます。
スペースを含むパスをコマンドに含めたときのエラー
まず普通の話から行きます。
コマンドラインでパスを含めるとき、スペースが入っているとまずいことになります。例えば「Drobo 5D」みたいな。
ls -l /Volumes/Drobo 5D/Art
パス中のスペースを命令文の区切りと解釈するのでエラーになります。そのため、スペースをエスケープします。「 \ 」を使いますね。
ls -l /Volumes/Drobo\ 5D/Art
ターミナルでは「テキストをエスケープしてペースト」というコマンドもあります。
あるいはダブルクォーテーション、クォーテーションで括ります。
ls -l "/Volumes/Drobo 5D/Art"
ls -l '/Volumes/Drobo 5D/Art'
ここまではどなたもご存じかと思います。問題は、コマンドを do script で送信するときです。
do script
AppleScriptからコマンドを実行させるには do script を使います。まったくド素人なので意味はわかりませんが、コマンド全体をダブルクォーテーションで括って送り込むようです。
tell application "Terminal" do script "ここにコマンド" end tell
do script 命令文は、いろんな情報によりますと以下のどれでも良いっぽいです。違いはわかりません。同じような挙動みたいです。
do script “コマンド”
do shell script “コマンド”
do script with command “コマンド”
※ あとで気づきましたが、do shell script は意味が異なり、難易度あがります。ターミナルを使わずシェルスクリプトを直接送り込むからのようです。
いずれにしろ、この命令文に、上記の例で挙げたコマンドを当てはめると、こうなると考えるわけですが
do script "ls -l /Volumes/Drobo 5D/Art"
これではエラーになります。スペースが入っているから当然そうなります。でもエスケープしてもエラーになります。
do script "ls -l /Volumes/Drobo\ 5D/Art"
どうしましょ。こうします。エスケープとかややこしいことを考えず、パスをクオートで括ればいいです。
do script "ls -l '/Volumes/Drobo 5D/Art'"
Appleの方法
AppleのDocumentation Archive: Mac Automation Scripting Guide > Calling Command-Line Tools によりますと、こうすればいいと書かれています。
tell application "Terminal" activate set thePath to quoted form of "パス" do script with command "コマンド" & thePath end tell
パスを変数 thePath に収めますが、このときクォートの指定を書き加えています。次に、”コマンド” のあと「 & 変数」と繋げます。コマンドの外で繋げるんですか。
上の実例を当てはめるとこうなります。
tell application "Terminal" activate set thePath to quoted form of "/Volumes/Drobo 5D/Art" do script with command "ls -l" & thePath end tell
ダブルクォーテーションで括ったコマンドの外で変数を繋げるというところが難しいところで、思いつきもしませんでした。ややもやもやします。
シンプルに
もやもやするのでもう少し考えみますと、クォーテーション指定で変数に収めたものが実際にどう出力されてるかというと、こうなってるんですね。
"'/Volumes/Drobo 5D/Art/'"
ということは、割とシンプルな話でしたか。
ややこしい変数を使わないとすれば、上で示したように普通にこう書くだけでした。
do script "ls -l '/Volumes/Drobo 5D/Art/'"
パスをクォーテーションで括ったコマンド全体をダブルクォーテーションで括っただけ
「素人が手を出すんじゃねえ」と脅されて逃げていたスペースを含むパス問題、ついに解決して、今後使える機能となりました。つまり、今日からあなたも私も、もはや素人ではなくなったのですね。毛の生えた素人になりました。
※ このすぐ下の章は、まだよく判っていなかった頃に Apple のお手本に沿って書こうとして難儀した話を挟んでいます。すっ飛ばしてその先 コマンドラインを do script “” に填め込むための整形 にお進みください。
少し複雑なコマンドでやさぐれてそして解決
前回の記事 stat で日付をゲット – ステイタスコマンド で書いたコマンドが上手に送れません。
stat -f "%N%T+++%B+++%m" パス/* | pbcopy
コマンドがあって、オプションがあって、フォーマットのテンプレパラメータがあって、パスがあって、パスの終わりはワイルドカードで、最後に pbcopy を繋げています。コードの中にダブルクォーテーションもあるし、これどうしましょ。
解決出来たと思い込んでいましたが理解力がなかったことが露呈しました。
— やさぐれ七転八倒を省略 —
と、この投稿ではこの後にいろんな書き方を試しては「これも駄目」「これもエラー」と、失敗コードを大量に書き倒して、最後に「なーんだ、こうだったのか」と物語風に書いていました。むしろそれがが当ポストのメイン部分だったのですが、読み返してみたら面白くもないし、無意味な描写がうざいので全部削除しました。最後のところだけ採用しておきます。
—- 続き —
あれこれ数時間格闘し、やはり毛の生えた程度の素人に do script は無理であったかと痛感し、やさぐれて不貞寝しました。
詐欺師になって偽物の同窓生を演じ、知らない人を懐かしんでいる夢を見て起きたら少し世界が変わっていました。
そもそもからやり直し
送りたいコマンドはこれでした。
stat -f “%N%T+++%B+++%m” パス/* | pbcopy
これを do script で送り込む正解はこうです。
do script "stat -f '%N%T+++%B+++%m' 'パス'* | pbcopy "
※パスは最後がスラッシュで終わっています。
何のことはない。コマンド全体をダブルクォーテーションで括るということに何の変更もなく、元々ダブルクォーテーションだったパラメータ部分とパス部分をシングルで括り直すだけの話でした。
何をややこしく考え違いしていたのだろうと後になって思いますが、人の学びというのはそういうものですよね。
まとめて書くとこうなります。
tell application "Terminal" do script "stat -f '%N%T+++%B+++%m' 'パス'* | pbcopy" end tell
※パスは最後がスラッシュで終わっています。
コマンドラインを do script “” に填め込むための整形
AppleScript の do script でコマンドラインを呼び出すとき、コマンドラインを以下のように整形すれば上手く嵌まります。
● パスはクォーテーションで括る。括るからエスケープしない
● ダブルクォーテーションで括ってある部分はシングルクォーテーションに変更する
以前は、” ” やら ‘ ‘ やら括られていない値を勝手に括り変えてはいけないと思い込んでいたために手も足も出ませんでした。
ついでにもうちょっと追加します。
- 複数行のコマンドラインの場合、改行を ” ; ” に置き換える
元のコードが複数行ということは、複数のコマンドを連続で実行しているということです。ですので、改行をコマンドの終わりを示す「 ; 」に置き換えます。
do script ” ” に填め込もうとするコマンドラインが複雑でも概ねこれで上手くいくと思います。
CodeManager 改善の兆し
全然関係ない個人的な話、上記の条件は計算式で作り出せますので、これは自作のコード管理&実行システムを作り直して改善が見込めます。
ファイルパスのスペースは、バックスラッシュ2つで解決できませんか?
“/Volumes/Drobo\\ 5D/Art”
あ。なるほど。それでいけますか。二重にエスケープするということですね。ご助言、ありがとうございます。
自動生成する際、パスにスペースが含まれるかどうかの条件分岐を設けるのが面倒で、結局シングルクォーテーションで括るということで済ませてます。