Bashコマンドと変数置換の混乱

Bashコマンドと変数置換の混乱

私は1989年以来、コマンドラインから1行のbashスクリプトを書いてきました。これらのスクリプトの形式は通常次のとおりです。

for name in 1 2 3; do wc $name.txt; done

私は2つのファイル名をパラメータとして使用するスクリプトを使用して、GIMPを使用していくつかの画像処理を実行しようとしています。有効なコマンドラインを提供する内容は次のとおりです。

/Applications/GIMP.app/Contents/MacOS/GIMP -b '(script-fu-overlay "png0004.tif" "png0000.tif") (script-fu-overlay "png0004.tif" "png0001.tif") (script-fu-overlay "png0004.tif" "png0002.tif") (script-fu-overlay "png0004.tif" "png0003.tif")'

これは、一般的なシングルライナーの助けを借りて作成されます。

X=\'$(for name in 0 1 2 3; do echo \(script-fu-overlay \"png0004.tif\" \"png000$name.tif\"\) ; done)\'

しかし…私の質問はこんな感じです。

echo /Applications/GIMP.app/Contents/MacOS/GIMP -b $X

上記の正確なコマンドラインを提供しますが、実行すると

/Applications/GIMP.app/Contents/MacOS/GIMP -b $X

引用符でファイル名を開くことができないというエラーが発生しますが、実行すると

/Applications/GIMP.app/Contents/MacOS/GIMP -b "$X"

すべてが正常です。私の実際の目標は、変数をまったく使用せずに1行のステートメントを作成できることを確認することです。それは次のとおりです。

/Applications/GIMP.app/Contents/MacOS/GIMP -b \'$(for name in 0 1 2 3; do echo \(script-fu-overlay \"png0004.tif\" \"png000$name.tif\"\) ; done)\'

しかし、これは明らかに同じ方法で失敗します

/Applications/GIMP.app/Contents/MacOS/GIMP -b $X

失敗する。

答え1

はい、シェル変数を使用せずにコマンドラインでこれを実行できます。

/Applications/GIMP.app/Contents/MacOS/GIMP -b "$(for name in 0 1 2 3; do echo \(script-fu-overlay \"png0004.tif\" \"png000$name.tif\"\) ; done)"

evalここでは、単一引用符をエスケープしないため、エスケープ処理をオフにする必要があります。必要な唯一のことは、コマンドが$(...)の周りの二重引用符を拡張して単語の分割が発生するのを防ぎ、結果を引数として渡すことですgimp

答え2

これはどうですか?

$ /Applications/GIMP.app/Contents/MacOS/GIMP -b \
"$(printf '%s\n' '(script-fu-overlay "png0004.tif" "png'{0..0004}'.png")')"

答え3

man bash説明する:

拡張順序は次のとおりです。中括弧の拡張、パラメータと変数の拡張、算術拡張、コマンドの置換(左から右へ)、パス名の拡張[...]これらの拡張を実行すると、引用符が表示されます。元の単語から自己引用されていない場合は削除されます(引用符を削除)。

したがって、ただ引用してください。元の単語から削除されました。ただし、引用は拡張後にのみ表示され、削除されません。

指定した場合は、ファイル名に引用符が必要ないため、これを保持できますが、ファイル名にスペースやその他の特殊文字が含まれていると問題が発生します。引用符やバックスラッシュは、この処理ステップでは引用符と見なされないため、役に立ちません。

「ワンステップ」要求を満たすように編集されました。

しかし、空白が含まれているファイルが複数あり、foo file fooその一部がbar file bar必要な場合はcatどうすればよいですか。

コマンドラインに表示されます

cat "foo file foo" "bar file bar"

ただし、それ以上の場合はスクリプトを作成する必要があります。

echo -n cat; for name in foo bar; do echo -n \ \"$name file $name\"; done

入力したい行を正確に生成しますが、

$(echo -n cat; for name in foo bar; do echo -n \ \"$name file $name\"; done)

上記と同じ理由で失敗します。すべてを二重引用符で囲むこと(あなたの場合に役立つgimp)も機能しません。新しいコマンドは単語で区切る必要がありますが、引用符で囲んではいけません。

それでは、コマンドラインに直接入力するのと同じように引用を実行するにはどうすればよいですか?文字列をシェルにコマンドとして渡します。

bash -c "$(echo -n cat; for name in foo bar; do echo -n \ \"$name file $name\"; done)"

これは、単一行でビルドコマンドを実行する最も一般的な方法です。

関連情報