546

一重引用符と一重引用符と変数内の他のコマンドを持つbashスクリプトからコマンドを実行したいと思います。

例えばrepo forall -c '....$variable'

この形式では、$はエスケープされ、変数は展開されません。

次のバリエーションを試しましたが、拒否されました。

repo forall -c '...."$variable" '

repo forall -c " '....$variable' "

" repo forall -c '....$variable' "

repo forall -c "'" ....$variable "'"

変数の代わりに値を代入すると、コマンドは問題なく実行されます。

どこが間違っているのか教えてください。

4

7 に答える 7

842

一重引用符内では、例外なくすべてが文字どおりに保持されます。

つまり、引用符を閉じて何かを挿入してから、もう一度入力する必要があります。

'before'"$variable"'after'
'before'"'"'after'
'before'\''after'

単語の連結は、並置によって単純に行われます。確認できるように、上記の各行はシェルに対する単一の単語です。引用符 (状況に応じて、一重引用符または二重引用符) は単語を分離しません。それらは、空白などのさまざまな特殊文字の解釈を無効にするためにのみ使用され$ます;...引用に関する優れたチュートリアルについては、Mark Reedの回答を参照してください。関連:bashでエスケープする必要があるのはどの文字ですか?

シェルによって解釈された文字列を連結しない

変数を連結してシェル コマンドを作成することは絶対に避けてください。これは、SQL フラグメントの連結 (SQL インジェクション!) と同様に悪い考えです。

通常、コマンドにプレースホルダーを含めることが可能であり、呼び出し先が呼び出し引数リストからそれらを受け取ることができるように、変数と共にコマンドを提供することができます。

たとえば、以下は非常に安全ではありません。これをしないでください

script="echo \"Argument 1 is: $myvar\""
/bin/sh -c "$script"

のコンテンツ$myvarが信頼されていない場合は、次のエクスプロイトがあります。

myvar='foo"; echo "you were hacked'

上記の呼び出しの代わりに、位置引数を使用してください。次の呼び出しの方が優れています。悪用できません。

script='echo "arg 1 is: $1"'
/bin/sh -c "$script" -- "$myvar"

への代入で単一のティックを使用していることに注意してくださいscript。これは、変数の展開やその他の形式の解釈なしで、文字どおりに取得されることを意味します。

于 2012-12-10T14:00:30.803 に答える
121

コマンドは、repoどのような引用符を取得するかを気にすることはできません。パラメータの拡張が必要な​​場合は、二重引用符を使用してください。それがあなたが多くのものをバックスラッシュしなければならないことを意味するなら、それの大部分に一重引用符を使用し、それからそれらから抜け出し、あなたが拡張が起こる必要がある部分のためにダブルスに入ります。

repo forall -c 'literal stuff goes here; '"stuff with $parameters here"' more literal stuff'

興味のある方は、以下に説明があります。

シェルからコマンドを実行すると、そのコマンドが引数として受け取るのは、nullで終了する文字列の配列です。これらの文字列には、 null以外の文字を絶対に含めることができます

ただし、シェルがコマンドラインから文字列の配列を構築している場合、一部の文字を特別に解釈します。これは、コマンドを簡単に(実際に、可能に)入力できるように設計されています。たとえば、スペースは通常、配列内の文字列間の境界を示します。そのため、個々の引数は「単語」と呼ばれることもあります。しかし、それでも引数にはスペースが含まれている場合があります。あなたはただあなたが望むものであるシェルを伝えるための何らかの方法が必要です。

任意の文字(スペースまたは別の円記号を含む)の前に円記号を使用して、その文字を文字通り処理するようにシェルに指示できます。しかし、あなたはこのようなことをすることができますが:

reply=\”That\'ll\ be\ \$4.96,\ please,\"\ said\ the\ cashier

...面倒になることがあります。したがって、シェルは代替手段を提供します:引用符。これらには2つの主な種類があります。

二重引用符は「グループ化引用符」と呼ばれます。ワイルドカードとエイリアスが展開されるのを防ぎますが、ほとんどの場合、単語にスペースを含めるためのものです。パラメータやコマンドの拡張(によって通知されるようなもの$)のような他のことはまだ起こります。そしてもちろん、二重引用符の中に文字通りの二重引用符が必要な場合は、バックスラッシュする必要があります。

reply="\"That'll be \$4.96, please,\" said the cashier"

一重引用符はより厳格です。それらの間のすべては、バックスラッシュを含め、完全に文字通りに解釈されます。一重引用符の中に文字通りの一重引用符を入れる方法は絶対にありません。

幸い、シェルの引用符は単語の区切り文字ではありません。それ自体では、単語を終了しません。同じ単語内で、異なるタイプの引用符の間を含め、引用符に出入りして、目的の結果を得ることができます。

reply='"That'\''ll be $4.96, please," said the cashier'

そのため、これは簡単です。バックスラッシュははるかに少なくなりますが、クローズシングルクォート、バックスラッシュリテラルシングルクォート、オープンシングルクォートのシーケンスには多少の慣れが必要です。

最新のシェルでは、POSIX標準で指定されていない別の引用符スタイルが追加されています。このスタイルでは、先頭の単一引用符の前にドル記号が付いています。このように引用された文字列は、Cプログラミング言語のANSI標準バージョンの文字列リテラルと同様の規則に従うため、「ANSI文字列」および$'...'ペア「ANSI引用符」と呼ばれることもあります。このような文字列内では、文字通りバックスラッシュが使用されることに関する上記のアドバイスは適用されなくなります。代わりに、それらは再び特別になります-それにバックスラッシュを付加することによって文字通りの一重引用符またはバックスラッシュを含めることができるだけでなく、シェルはANSI C文字エスケープを拡張します(改行\n\tタブ、および\xHH16進コードHH)。ただし、それ以外の場合は、単一引用符で囲まれた文字列として動作します。パラメーターまたはコマンド置換は行われません。

reply=$'"That\'ll be $4.96, please," said the cashier'

reply注意すべき重要な点は、変数に格納される単一の文字列は、これらすべての例でまったく同じであるということです。同様に、シェルがコマンドラインの解析を完了した後、実行中のコマンドが、プログラムで作成されるのではなく、各引数文字列が実際にどのように入力されたか、または入力された場合でも正確に通知する方法はありません。

于 2012-12-11T12:02:20.847 に答える
0

変数には一重引用符を含めることができます。

myvar=\'....$variable\'

repo forall -c $myvar
于 2018-06-24T01:52:14.377 に答える