これらの回答のほとんどは、あなたが尋ねている特定のケースに当てはまります。任意のは、あなたが例えば、SSH経由で、シェル拡張の複数の層を通じてbashのコマンドを引用する必要がある場合に引用することができます友人と私が開発していることを一般的なアプローチがありsu -c
、bash -c
一方のコアは、ここで、あなたが必要とする原始的な存在である、などがネイティブbashの場合:
quote_args() {
local sq="'"
local dq='"'
local space=""
local arg
for arg; do
echo -n "$space'${arg//$sq/$sq$dq$sq$dq$sq}'"
space=" "
done
}
これはまさにそれが言うことを行います:それは各引数を個別にシェルクォートします(もちろんbash展開後):
$ quote_args foo bar
'foo' 'bar'
$ quote_args arg1 'arg2 arg2a' arg3
'arg1' 'arg2 arg2a' 'arg3'
$ quote_args dq'"'
'dq"'
$ quote_args dq'"' sq"'"
'dq"' 'sq'"'"''
$ quote_args "*"
'*'
$ quote_args /b*
'/bin' '/boot'
それは、拡張の1つの層に対して明白なことを行います。
$ bash -c "$(quote_args echo a'"'b"'"c arg2)"
a"b'c arg2
(周りの二重引用符$(quote_args ...)
は、結果をへの単一の引数にするために必要bash -c
です。)そして、より一般的に使用して、複数の展開層を適切に引用することができます。
$ bash -c "$(quote_args bash -c "$(quote_args echo a'"'b"'"c arg2)")"
a"b'c arg2
上記の例:
- shellは、各引数を内部で
quote_args
個別に引用し、結果の出力を単一の引数に内部の二重引用符で結合します。
- シェル重引用符
bash
、-c
およびステップ1から既に一度、引用された結果は、その後、外側の二重引用符と単一の引数に結果を兼ね備えています。
- その混乱を引数としてアウターに送信し
bash -c
ます。
それが一言で言えばアイデアです。これでかなり複雑なことを行うことができますが、評価の順序と引用される部分文字列に注意する必要があります。たとえば、以下は間違った処理を行います(「間違った」の定義の場合)。
$ (cd /tmp; bash -c "$(quote_args cd /; pwd 1>&2)")
/tmp
$ (cd /tmp; bash -c "$(quote_args cd /; [ -e *sbin ] && echo success 1>&2 || echo failure 1>&2)")
failure
最初の例では、bashはすぐquote_args cd /; pwd 1>&2
に2つの別々のコマンド、quote_args cd /
およびに展開されるpwd 1>&2
ため、CWDはコマンドが実行された/tmp
ときのままpwd
です。2番目の例は、グロビングの同様の問題を示しています。実際、すべてのbash展開で同じ基本的な問題が発生します。ここでの問題は、コマンド置換が関数呼び出しではないことです。これは、文字通り1つのbashスクリプトを評価し、その出力を別のbashスクリプトの一部として使用することです。
単にシェル演算子をエスケープしようとすると、渡される結果の文字列はbash -c
、演算子として解釈されない個別に引用された一連の文字列なので、失敗します。bashに渡されました:
$ (cd /tmp; echo "$(quote_args cd /\; pwd 1\>\&2)")
'cd' '/;' 'pwd' '1>&2'
$ (cd /tmp; echo "$(quote_args cd /\; \[ -e \*sbin \] \&\& echo success 1\>\&2 \|\| echo failure 1\>\&2)")
'cd' '/;' '[' '-e' '*sbin' ']' '&&' 'echo' 'success' '1>&2' '||' 'echo' 'failure' '1>&2'
ここでの問題は、あなたが引用しすぎているということです。必要なのは、演算子を囲んbash -c
でいるへの入力として引用符で囲まないようにすることです。つまり、演算子は$(quote_args ...)
コマンド置換の外側にある必要があります。
したがって、最も一般的な意味で行う必要があるのは、コマンド置換時に個別に展開されることを意図していないコマンドの各単語をシェル引用し、シェル演算子に余分な引用を適用しないことです。
$ (cd /tmp; echo "$(quote_args cd /); $(quote_args pwd) 1>&2")
'cd' '/'; 'pwd' 1>&2
$ (cd /tmp; bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")
/
$ (cd /tmp; echo "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")
'cd' '/'; [ -e *'sbin' ] && 'echo' 'success' 1>&2 || 'echo' 'failure' 1>&2
$ (cd /tmp; bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")
success
これを実行すると、文字列全体が任意のレベルの評価にさらに引用するための公正なゲームになります。
$ bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")"
/
$ bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")")"
/
$ bash -c "$(quote_args bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")")")"
/
$ bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")"
success
$ bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *sbin ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")")"
success
$ bash -c "$(quote_args bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")")")"
success
等
これらの例は、のような言葉ことを考えると神経が高ぶった見えるかもしれませんsuccess
、sbin
とは、pwd
シェル引用されている必要はありませんが、任意の入力を取ってスクリプトを記述する際に覚えておくべき重要なポイントは、あなたが絶対にわからない、すべて引用したいということです」doesnのをtはユーザがスローされたときに、あなたが知っていることはありませんので、引用必要ですRobert'; rm -rf /
。
カバーの下で何が起こっているかをよりよく理解するために、2つの小さなヘルパー関数をいじることができます。
debug_args() {
for (( I=1; $I <= $#; I++ )); do
echo -n "$I:<${!I}> " 1>&2
done
echo 1>&2
}
debug_args_and_run() {
debug_args "$@"
"$@"
}
コマンドを実行する前に、コマンドの各引数を列挙します。
$ debug_args_and_run echo a'"'b"'"c arg2
1:<echo> 2:<a"b'c> 3:<arg2>
a"b'c arg2
$ bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)"
1:<echo> 2:<a"b'c> 3:<arg2>
a"b'c arg2
$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'>
1:<echo> 2:<a"b'c> 3:<arg2>
a"b'c arg2
$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'echo'"'"' '"'"'a"b'"'"'"'"'"'"'"'"'c'"'"' '"'"'arg2'"'"''>
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'>
1:<echo> 2:<a"b'c> 3:<arg2>
a"b'c arg2
$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")")")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'bash'"'"' '"'"'-c'"'"' '"'"''"'"'"'"'"'"'"'"'debug_args_and_run'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'echo'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'a"b'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'c'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'arg2'"'"'"'"'"'"'"'"''"'"''>
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'echo'"'"' '"'"'a"b'"'"'"'"'"'"'"'"'c'"'"' '"'"'arg2'"'"''>
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'>
1:<echo> 2:<a"b'c> 3:<arg2>
a"b'c arg2