pbcopy(クリップボードに保存するコマンド)が、ターミナルでは問題ないのに do shell script では動かない事例。問題がどこにあるのか、解決できるのか、デジタルデカが挑みます。
Introduction
何の話をしているかをちょっとだけ前振りで書いておきます。
ターミナルにUNIXの呪文をタタタッとタイプして命令を実行させる行為がありますね。この命令文や命令を書く行為を何と呼ぶのか知りません。でも便利に使ってます。
それを AppleScript から行うことが出来ます。do script を書きます。
tell application "Terminal" activate do script "ここにUNIX命令文" end tell
これは、AppleScriptからターミナルを呼び出して操作しています。
もう一歩進めると、ターミナルを介さず直接命令を送り込むというチャレンジになります。do shell script を使います。
do shell script "ここにUNIX命令文"
ターミナル上でもそうでなくても大した違いはあるまい。と思っていたら大間違いで、難易度が全然違います。
さて、命令をすれば結果が返ってくるのが世の常です。ターミナルでは結果がターミナルの画面に表示されます。クリップボードに収めることも出来ます。
pbcopy はクリップボードに収めるコマンドです。パイプというもので繋げて、結果をクリップボードにコピーします。
例えばターミナルで date というコマンドに pbcopy を繋げてリターンすると、クリップボードに日付が入ります。
date | pbcopy
do shell script だと次のように書きますね。
do shell script "date | pbcopy"
AppleScriptのスクリプトエディタに書いて実行させると動きが確認できます。日付がコピーされていると思います。
ということで、この pbcopy を使いまくっているわけですが、ターミナルでは問題ないのに、do shell script では問題が発生します。
pbcopy の問題
do shell script では pbcopy が含まれていると結果がなくなってしまう事例が多発、最初に気づいたのは Exiftool でした。
Exiftool はインストールして使うツールです。そういう場合、do shell script ではコマンドを書くだけでは動かず、パスを書かねばなりません。ターミナルでは「exiftool 〜」と書きますが、do shell script では「/usr/local/bin/exiftool 」と書く必要があります。 参照: ターミナルで動くのにAutomatorのシェルではエラー
Exiftool の結果を pbcopy すると、結果が消滅するんです。
ターミナルやdo script で書くともちろん期待通りに動きます。
/* ターミナル */
exiftool 目的パス | pbcopy
/* tell付きの do script */
do script "exiftool '目的パス' | pbcopy"
どちらも、クリップボードに結果が格納されました。
do shell script の pbcopy で結果が消失
これを do shell script にすると消えます。
/* do shell script */
do shell script "/usr/local/bin/exiftool '目的パス' | pbcopy"
スクリプトエディタで実行してみても、結果が消えていることが確認できます。これは一体何事でしょう。
先ほど「date | pbcopy」の例を挙げましたが、それはちゃんと動きました。ちゃんと動くときと動かないときがあるとわかります。
exiftool の問題、あるいは外部ツールであることが問題を引き起こしているのでしょうか。まずそう考えてしまうのが筋ですよね。少し試してみましょう。内部というかデフォルトのコマンド、find や stat で試しました。
stat と find で試す
正しい挙動でした。結果がクリップボードに収まっています。ではやはりExiftoolもしくは外部ツールの問題であったか。と、簡単に決めつけてはいけません。stat や find でも問題が発生しました。
結果に日本語が含まれると文字化け
まずこれをご覧ください。pbcopy は動きはしています。しかしよく見るとファイル名が文字化けしています。
怪しいです。とても怪しいです。ここに、問題の根っこが潜んでいそうです。
コマンドのパラメータに日本語が含まれると完全に消滅
さらにテストしてみました。日本語フォルダを指定すると完全に消失することが確認できました。
Exiftoolの問題とかそういう話ではなく、コマンドのパラメータに日本語が含まれていれば完全に消滅、結果に日本語が含まれていると文字化けすることが判明しました。
ターミナルでは賢く判別してくれますが、生のシェルスクリプトではハチャメチャになってしまうという結論です。
このハチャメチャの原因はどこにあるんでしょう。コマンドを処理するレベルなのか、パイプなのか、はたまた pbcopy なのか。「何故」の前に「どこ」で問題が起きているのかを知りたいです。
コマンドか、pbcopyか
pbcopyを使わないテスト
はい。テストやってみました。pbcopyを使わずに実行させれば分かります。
どちらも正常運転。またまた完璧に明白になりました。pbcopy の仕業です。
犯人は pbcopy
真犯人が明らかになり、Exiftool の容疑もすっかり晴れました。pbcopy がクリップボードを扱う際、ランゲージが気に入らなければ消してしまうようです。真の動機は不明ですが、何が起きているのかはわかりました。
解決方法
pbcopy を改めて見てみましょう。man を開くと、Encoding について書かれている箇所がありました。しかしあれですね、man ページって、変なところで改行してるから翻訳ソフトにかけるときに面倒ですよね。それはともかく、大体このようなことが書かれていました。
pbcopyとpbpaste はロケール環境変数を使用してエンコードを決定します。指定がない場合は標準のCエンコーディングが使用されます。ターミナルではUTF-8を使用し、適切に処置されます。
なるほどよく分かりました。ターミナルでは自動で賢く振る舞うけどそうじゃなければエンコーディングを指定しなければいけなかったんですね。
環境変数とやらを指定すれば良さそうです。「LANG=en_US.UTF-8」のように指定するのだとか。これを追加すればいいのか。
早速、pbcopy にLANG=en_US.UTF-8を追加したいわけですが、man の OPTION を見ても、その指定について何も書かれていません。どう書けばいいんでしょう。
pbcopy LANG=en_US.UTF-8 と書いても駄目でした。
pbcopy -LANG=en_US.UTF-8 とマイナスを付けてみても駄目。はて。どうしましょう。どうやって指定すれば良いのかさっぱりわかりません。その答えは後ほど。その前に別の解決方法を書いておきます。
解決しない解決方法
なんだかよく分からないので、エンコーディングは後回しにして別の解決方法を取りますか。
それは、pbcopy をコマンドに含めずに、AppleScriptでクリップボードに収めることです。
pbcopy と同じ機能のAppleScriptがあります。
set the clipboard です。
set the clipboard を上手く使うために、そもそもの do shell script も変数として作動させ、その変数をクリップボードに収めるのが賢そうな書き方になりますか。
set dd to do shell script "find '/Volumes/Drobo5D/テストフォルダ'"
set the clipboard to dd
バッチリ正しく動きました。
変数を使わず set the clipboard to だけでもシンプルに書けます。そうするとスクリプトエディタでは括弧を自動で付けてきますね。
set the clipboard to (do shell script "find '/Volumes/Drobo5D/テストフォルダ'")
真の解決ではないですが、こういう解決方法もあるということで。
個人的にはこれでは却下なんです。というのも、話が脱線しますがFileMakerでコード管理と実行のファイルを作っていて、そのファイルの中で自動でシェルコマンドをAppleScriptの実行形式に変換しています。pbcopy を使う使わないは個々のコードによるもので、コードの外にある部分でクリップボードの処理を行うことが不適切なんです。→ Macで実行可能なFileMakerコード管理
ということでどうしても pbcopy を使いたいというあなたと私のために、解決する方法を以下に書いておきます。
pbcopy にLANGを付け加える
少し↑で、pbcopy にエンコーディング指定する書き方が分かりませんでしたが、いろいろ検索したあげくに答えを見つけましたので書いておきます。大きな文字でいきますね。
LANG=en_US.UTF-8 pbcopy
前に書くのか。それは思いつかんかった。
ぐぐって見つけたページはこれですが、https://apple.stackexchange.com/questions/414242/clipboard-and-class-issue-when-running-applescript-do-shell-script おかげさまで書き方が分かりました。
実際にはUTF-8さえ指定できればいいと思うんですが、UTF-8だけを指定する方法は分かりませんでした。
日本語の指定はこうですが、enでもjaでも同じように問題なく作動します。
LANG=ja_JP.UTF-8
ということで、忙しいさなかにまた変なことで嵌まってしまい一晩を無駄にしてしまいましたが、また少し知見が深まりました。
CodeManager、Media管理、その他同じ仕組みを使ってる関係するファイルをまたまた作り直しです・・・