FileMaker CodeManager シングルクォートの改善

FileMaker から AppleScript や コマンドを実行できる CodeRun という仕組みがありまして、その機能にコマンドメモなどの機能を加えた CodeManager というファイルがあります。更新版をアップしました。

CodeManager v3.2

ここの筆者の手作り、自家製ファイル CodeManager というのがあります。コマンドやAppleScript の覚え書きメモと、直接実行できるランチャー機能を兼ね備えた FileMaker Pro のファイルです。その話をブログに書いたときにダウンロードできるようにしてそのままだったので改めて更新ファイルをアップします。

ファイルはこちらです。


Download CodeManager_v3-2.zip

Mac専用です。
macOS 10.14 Mojava 以上、FileMaker Pro 19 以上で動作します。
あらかじめいくつかのサンプルレコードが入っています。
2023.12.20 update

CodeManager というものが何なのかは過去記事にあります。

Macで実行可能なFileMakerコード管理 CodeManager

CodeManager その2 コマンドラインを do shell script に変換

CodeManager その3 実行するスクリプト

CodeManager 使い方

別口で FileMaker で画像や映像のカタログを作る メディア管理の作り方 というシリーズ記事の実作ファイルがあります。その中でも CodeRun.fmp12 として簡易版を使用しています。

メディア管理のシリーズと違って CodeManager の話は公開目的ではないので更新したからといっていちいち報告したり新しいファイルをアップしたりしてこなかったんですが、ちょっと久しぶりに上げておきます。

適当・ゆるいコードランチャー

CodeManager の中に CodeRun というテーブルがあって、コードランチャーの役割を担っています。テンプレートがあって、パラメータを個別に入力することで完成コードを生成して動かします。

ただ、ここの筆者の低いレベルに合わせた低レベルのランチャーなので、複雑なコードは扱えません。複雑なやつはそれなりにこんなの使わずにやりくりします。つまり、けっこうてきとうです。

適当さの最たるものが、これまでパスを扱うときにファイル名にシングルクォート(')が含まれていると動作しませんでした。

CodeRun シングルクォートのエスケープ

別の投稿でシングルクォートのエスケープについて書いたので、それに合わせて CodeRun の仕組みも修正しています。エスケープの概要については以下の投稿をご覧ください。

シングルクォートのエスケープ処理

シングルクォートのエスケープ処理

上の投稿内容をざっくりまとめると、シングルクォートがある場合のエスケースの方法は以下の通りです。例として「Don't Breathe.txt」を処理します。

素で書くとき

echo Don\'t\ Breathe.txt 

\」を加えた普通のエスケープを行います。

ダブルクォートの場合

echo "Don't Breathe.txt"

ダブルクォートで括れば何もする必要ありません。

シングルクォートの場合

echo 'Don'\''t Breathe.txt'

シングルクォートで括った中では、''\'' で置き換えます。

do shell script のダブルクォート内

do shell script "echo 'Don'\\''t Breathe.txt'"

do shell script のダブルクォートの中では、''\\'' で置き換えます。エスケープをエスケープするんです。

CodeRun の計算式内に書く場合

Substitute ( "Don't Breathe.txt" ; "'" ; "'\\\''" );

CodeRun の計算式内では、''\\\'' で置き換えます。エスケープのエスケープをエスケープするんです。

 

ということで、上記投稿で話は終わっているのですが、その続きというか補足を少し。

param置換のカスタム関数

CodeRun ではレコードにテンプレートを書き入れます。

簡単な例だとこんな感じです。

mv $POSIX1 $POSIX2

他にパラメータのフィールドが複数あって、param_posix1 とか param_posix2 とか param_text1 とか、そんな名前のフィールドです。そのパラメータフィールドに、この例では入力パス($POSIX1)と出力パス($POSIX2)を書きますと、計算で do shell script を作り上げます。以下が完成コード例です。

do shell script "mv '/Users/user/Desktop/Don'\\''t Breathe.txt' '/Users/user/Desktop/Dont_Breathe.txt' &"

最後はこれをスクリプトステップ「AppleScriptを実行」させると。

手順としては、テンプレフィールドのコードを do shell script 化するする工程のあと、最後に $POSIX1 などの仮置き変数を実際の値に置き換える置換の工程を経ます。

この最後の置換は substitute 関数を使った置き換えで、単純な計算ですがカスタム関数化しています。

この部分に、シングルクォートの処理を挟み込みました。

code>Let ( [

$posix1 = CodeRun::param_POSIX1 ;
$posix2 = CodeRun::param_POSIX2 ;
$text1 = CodeRun::param_TEXT1 ;
$text2= CodeRun::param_TEXT2 ;
$text3 = CodeRun::param_TEXT3 ;
$text4 = CodeRun::param_TEXT4 ;

$posix1 = Substitute ( $posix1 ; "'" ; "'\\\''" );
$posix2 = Substitute ( $posix2 ; "'" ; "'\\\''" );
$text1 = Substitute ( $text1 ; "'" ; "'\\\''" );
$text2 = Substitute ( $text1 ; "'" ; "'\\\''" );
$text3 = Substitute ( $text1 ; "'" ; "'\\\''" );
$text4 = Substitute ( $text1 ; "'" ; "'\\\''" )

]; 

Substitute ( codeTemp ;
 [ "$POSIX1" ; $posix1 ] ;
 [ "$POSIX2" ; $posix2 ] ;
 [ "$TEXT1" ;  CodeRun::param_TEXT1 ] ;
 [ "$TEXT2" ;  CodeRun::param_TEXT2 ] ;
 [ "$TEXT3" ;  CodeRun::param_TEXT3 ] ;
 [ "$TEXT4" ;  CodeRun::param_TEXT4 ] ;
 [ "$NUM1" ;  CodeRun::param_NUM1 ] ;
 [ "$NUM2" ;  CodeRun::param_NUM2 ] ;
 [ "$NUM3" ;  CodeRun::param_NUM3 ] ;
 [ "$NUM4" ;  CodeRun::param_NUM4 ] ;
 [ "$OPTION" ; CodeRun::param_OPTION ] 
)

)

パラメータのフィールドを無駄に作りすぎたのでそのすべてをチクチク書いており冗長になってしまってますが💦

これまで、CodeRun ではファイル名に ‘ が含まれるパスを指定するとエラーで動きませんでしたが、そういう不手際が解消されました。

ただ、それでも緩くててきとうなコードランチャーでありますので、別の文字列ではまた別のエラーが出てくるかもしれません。完璧なんてあり得ませんし。そのあたりは割り引いて使ってください。

その他の更新

CodeManager というファイルは筆者も日常的に使っているので、都度更新しています。以前の投稿で公開していたファイルから随分と変化しているかもしれません。でも更新内容をいちいち記録もしていないので詳細を示すことはできません。

do shell script 化の計算式

作った本人が見てもため息が出るような冗長で汚い計算式です。失敗を発見する度に修繕を重ねた結果こうなりました。整理し直したいと思いつつ、そんな暇ないのでとりあえず放置中です。

do shell script化の計算式

// CodeTemp を do shell script に変換する

Let ( [
code = CodeRun::codeTemp ;

// テンプレート DoShellScript を使用
template = CodeRun::Template_DoShellScript ;

// パスワード
password = CodeRun::password ;

// シングルの中のダブルクォートをエスケープ処理。それ以外のダブルクォートをシングルに変換
code =
While ( [

code = CodeRun::codeTemp;
codeOut = CodeRun::codeTemp;
sq = Substitute ( code ; [ " '" ; ¶ & "'" ];[ "' " ; "'" & ¶ ] );
c = 1;
vc = ValueCount ( sq )

] ; c ≤ vc ; [

gv = GetValue ( sq ; c ) ;
gv2 = If ( Left ( gv ; 1 )="'" ; Substitute ( gv ; "\"" ; "\\\"" );
Substitute ( gv ; "\"" ; "'" ) );
codeOut = Substitute ( codeOut ; gv ; gv2 );

c = c+1

] ; codeOut );

// 外部ツールのパス( exiftool, ffmpeg )

comPath = 外部ツールの場所 ;

exiftoolPath = If ( not IsEmpty ( comPath ) ; comPath & "exiftool"; exiftoolの場所 & "exiftool" ) ;
ffmpegPath = If ( not IsEmpty ( comPath ) ; comPath & "ffmpeg"; ffmpegの場所 & "ffmpeg" ) ;
ffprobePath = If ( not IsEmpty ( comPath ) ; comPath & "ffprobe"; ffmpegの場所 & "ffprobe" ) ;
ffplayPath = If ( not IsEmpty ( comPath ) ; comPath & "ffplay"; ffmpegの場所 & "ffplay" ) ;

code =
If ( PatternCount ( code ; "exiftool " ) ; Substitute ( code ; "exiftool " ; "'" & exiftoolPath & "' " ) ; code ) ;

code =
If ( PatternCount ( code ; "ffmpeg " ) ; Substitute ( code ; "ffmpeg " ; "'" & ffmpegPath & "' " ) ; code ) ;

code =
If ( PatternCount ( code ; "ffprobe " ) ; Substitute ( code ; "ffprobe " ; "'" & ffprobePath & "' " ) ; code ) ;

code =
If ( PatternCount ( code ; "ffplay " ) ; Substitute ( code ; "ffplay " ; "'" & ffplayPath & "' " ) ; code ) ;

// pbcopy にLANGを追加する
code = Substitute ( code ; "pbcopy" ; "LANG=ja_JP.UTF-8 pbcopy" ) ;

// コマンドに sudo があるとき、パスワードを含める(セキュリティがばがばにつき注意)
code = If ( PatternCount ( code ; "sudo " ); Substitute ( code ; "sudo " ; "echo '" & password & "' | sudo -S " ) ; code );

// ダブルクォーテーションがあればシングルに変更
// エスケープされたものを除く
code = Substitute ( code ; [ " \"" ; " '" ] ) ;

// パスを囲む(POSIX クォーテーション付け忘れがたまにあるのでその補正)

code = If ( PatternCount ( code ; " $POSIX1 " ) ; Substitute ( code ; [ "$POSIX1" ; "'$POSIX1'" ] ) ; code ) ;
code = If ( PatternCount ( code ; " $POSIX2 " ) ; Substitute ( code ; [ "$POSIX2" ; "'$POSIX2'" ] ) ; code ) ;
code = If ( PatternCount ( code ; " $POSIX1" ) ; Substitute ( code ; [ "$POSIX1" ; "'$POSIX1'" ] ) ; code ) ;
code = If ( PatternCount ( code ; " $POSIX2" ) ; Substitute ( code ; [ "$POSIX2" ; "'$POSIX2'" ] ) ; code ) ;

// 複数行の処理
code = If ( PatternCount ( code ; ¶ ); Substitute ( code ; ¶ ; ";" ) ; code ) ;
code = If ( Right ( code ; 2 )=" ;" ; Left (code ; Length ( code ) -2 ) ; code );

// シングルクォーテーションが重なっていれば修正する
// code = Substitute ( code ; "''" ; "'" ) ;

// 1行スクリプトのバックグラウンド処理(オマケ的。不要なら削る)
code = If ( ValueCount (CodeRun::codeTemp) = 1 and Right ( code ; 1 )≠ "&" ; code & " &" ; code );

// 最終。commandを置換
code = Substitute ( template ; "$COMMAND" ; code );

// さらに最終。category が command の場合のみ採用(それ以外は空)

code = If (CodeRun::Category = "Command" ; code )
];
code
)

多分、計算する順番というか処理の順番を変更した覚えがあります。不手際がまだ残ってるんで腕に自信のある方は修正してください💦

外部ツールのパス

Exiftool や FFmpeg など外部ツールをインストールした場合はインストールしたフォルダを記録します。「外部ツールのフォルダパス」フィールドに書いておけば良いのですが、特殊なフォルダにインストールした場合はそれを記録します。優先順位を付け(ツールごとのフォルダを優先)、コマンドが「見つかりません」にならないようにしました。

鍵アイコンのボタンを設置して、レコードを変更不可にできるようにしました。

CodeManager は他のファイルから参照することもあるので、他で使用しているレコードをうっかり変更できないようにする措置です。

 

メディア管理の作り方でCodeRunを使っていると、ちょっと根本的に別のアプローチでコードを生成・実行する仕組みが欲しくなってきます。次の改良では「他のFiileMaker書類から指示してコードを作る」ことがやりやすい仕組みってのを念頭に作り変えるかもしれません。

 

そんなこんなで、このファイルにはまだまだ不具合や不出来が多く残っていますが、そこそこ使い物になってもいます。

 

コメント

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください