コマンドの記述で、シングルクォートの中にシングルクォートがある場合のエスケープ処理が少し厄介です
テキスト内容に ‘(シングルクォート)が含まれる場合の扱いについてです。何の扱いかというと、ターミナルのコマンドとかシェルスクリプトですが、そこから派生して do shell script や FileMaker の CodeRun に及びます。
そこに行き着くまでに順を追う必要がありますのでゆっくり行きます。
シングルクォートのエスケープ
シングルクォートは'
です。これがファイル名(パス)に含まれていたり、何らかの使用するテキスト内容に含まれている場合、ターミナルのコマンドやその他でエスケープ処理をしなければなりません。
例として「Don’t Breathe.txt」というファイルをターミナルやいろんなところで使ってみましょう。
パスは /Users/user/Desktop/Don't Breathe.txt
です。
ターミナルでシングルクォートエスケープ
素の状態
ターミナルはかゆいところに手が届く機能を有していますので、パスにシングルクォートがあってもファイルをドラッグドロップすればきっちりエスケープしてくれます。
ターミナルに「echo 」と書いてその後ろにファイルをドロップしてみましょう。
echo /Users/user/Desktop/Don\'t\ Breathe.txt
エスケープしてくれました。
- スペース「 」を「\ 」
- シングルクォート「’」を「\’」
と、普通に \ でエスケープしてくれているのが判ります。
パスをコピーしてから編集メニュー「テキストをエスケープしてペースト」を実行しても同じ結果になります。
ダブルクォート
気の利いたターミナルを頼らない場合、よく使う手はダブルクォーテーションで括ることかと思います。ダブルクォーテーションで括るとエスケープ処理は不要です。
echo "/Users/user/Desktop/Don't Breathe.txt"
これで問題ありません。
シングルクォート
筆者は訳あってこういう用途でダブルクォーテーションを使いません。シングルクォートを使います。
シングルクォートは力強い括りで、ダブルクォート以上に内部のテキストをテキストとして扱います。ただし例外は内部のシングルクォートです。
// 失敗例 echo '/Users/user/Desktop/Don't Breathe.txt'
これはもちろんNGです。上手く行きません。シングルクォートで括ってるその中にシングルクォートが存在しているからです。これをエスケープする必要があります。
「 \’ 」と普通のエスケープをすればどうでしょう。
// 失敗例 echo '/Users/user/Desktop/Don\'t Breathe.txt'
シングルクォートの中で普通のエスケープしても効きません。エラーでNGです。
実はシングルクォートの中のシングルクォートをエスケープする方法はありません。ではどうするかというと、シングルクォートで文字列を分割します。
Don’t Breathe.txt なら、「Don」と「’」と「t Breathe」 に分けます。つまりこうです。ファイル名の部分だけ書きますね。色も付けときました。
'Don'\''t Breathe.txt'
ターミナルでは文字列の連結をただ連ねて書くだけで良いということですのでこうなります。
理屈を踏まえず結果だけ着目すると、シングルクォートを含む文字列は次のルールで置換すれば良いとわかります。
- シングルクォート「
'
」を「'\''
」で置き換える
ターミナルを使用するだけなら、ファイルのドロップやダブルクォートの使用で十分かもしれません。シングルクォートを取り扱ったのは、do shell script を使うからです。
do shell script でシングルクォートエスケープ
AppleScript からコマンドを呼び出す do shell script では、コマンド全体をダブルクォーテーションで括ります。
do shell script "コマンド"
よって、コマンドの中にダブルクォートを使えません。パスなど、括りたい文字列はシングルクォートで括ります。
do shell script "echo '/Users/user/Desktop/file.txt'"
こんなふうになりますが、パスにシングルクォートが含まれる場合はどうなりましょう。先ほどのターミナルの例に倣うと、シングルクォート「'
」を「'\''
」で置き換えれば良いと考えるかもしれません。
// 失敗例
do shell script "echo '/Users/user/Desktop/Don'\''t Breathe.txt'"
こうですね。でもこれでは構文エラーになりNGです。
エスケープをエスケープする
随分考え込んだんですが、do shell script がダブルクォートの中のエスケープを素のエスケープと認識したんではないかと思います。ダブルクォーテーションってのはシングルほど強くなく、内部でコードを展開するという個性があります。なので試しにエスケープ「\
」をエスケープ「\\
」してみました。
do shell script "echo '/Users/user/Desktop/Don'\\''t Breathe.txt'"
正解。これで動きました。
do shell script を作成する場合のシングルクォートの扱いは、結果にだけ着目すると次の通りとなりました。
- シングルクォート「
'
」を「'\\''
」で置き換える
これで大概うまくいきます。
さて大体汎用的な話は以上でお仕舞いです。ここからは、ここDigitalboo限定しかもFileMaker限定さらにCodeRun限定の話になりますので、関係ない方はここまででございます。ありがとうございました。
CodeRun カスタム関数をシングルクォートに対応
CodeRunはFileMaker書類で、AppleScriptやターミナルコマンドを実行させるランチャーです。作者の知識の乏しさ故、簡単なコマンドにのみ対応する汎用性の少ないファイルですが、この中でこれまでシングルクォートに未対応でした。どうすればいいのか判らなかったからです。
今は少しわかったので、中の仕組みを修正しています。
CodeRun のコード生成計算式でシングルクォート
CodeRun にはパラメータのフィールドが複数ありまして、そこに書いた内容に応じてコードを自動生成する仕組みです。計算式は主に検索置換関数を使っています。その関数をカスタム関数にして計算式内で使用しており、そこをまず修正します。
最初の失敗例
フィールド内に「'
」があれば、上の do shell script のルールに従って、「'\\''
」 に置き換えればいいのですね。と、例えばこのように修正しましたが。
// 失敗例
$posix1 = Substitute ( $posix1 ; "'" ; "'\\''" );
失敗です。動きません。しばらく考え込みました。
CodeRun では、この置換関数の結果を含め、計算式でコード全体を作っています。ということは、何が起きたかというと、その計算式内でエスケープを素のエスケープと解釈したと考えられます。
エスケープのエスケープをエスケープ
そこで、エスケープのエスケープをした部分をエスケープしてみます。
$posix1 = Substitute ( $posix1 ; "'" ; "'\\\''" );
正解。動きました。
関数の一部ですが、このように置換しておきます。
$posix1 = Substitute ( $posix1 ; "'" ; "'\\\''" );
$posix2 = Substitute ( $posix2 ; "'" ; "'\\\''" );
$text1 = Substitute ( $text1 ; "'" ; "'\\\''" );
$text2 = Substitute ( $text1 ; "'" ; "'\\\''" );
$text3 = Substitute ( $text1 ; "'" ; "'\\\''" );
$text4 = Substitute ( $text1 ; "'" ; "'\\\''" )
パラメータのフィールドが多すぎるんですがまあそれはそれとして。これはあくまで一部分のみの抜粋です。
CodeRun は MediaManager というファイルの内部にあります。これを修正して近いうちにダウンロードできるようにしておきます。
CodeRun の簡易版を メディア管理 のDBTools内に置いて使用しています。こちらも修正して、メディア管理のダウンロードファイルを置き換えておきます。