web-dev-qa-db-ja.com

Bashのコマンド内の単一引用符内の変数の拡張

bashシェルスクリプト からのコマンドを実行したいのですが、シングルクォートと、シングルクォートと変数の中に他のコマンドがあります。

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

このフォーマットでは、$はエスケープされ、変数は展開されません。

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

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

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

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

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

変数の代わりに値を代入しても、コマンドは正常に実行されます。

どこで問題があるのか​​教えてください。

292
Rachit

一重引用符の中には、例外なく例外なく文字通りすべてが保存されています。

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

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

確認できるように、上記の各行はシェルへの単一の単語です。文字列の連結は単純に並置によって行われます。引用符(状況に応じて一重引用符または二重引用符)は、空白文字、$;...のようなさまざまな特殊文字の解釈を無効にするために使用されます。 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への代入でシングルティックが使用されていることに注意してください。これは変数展開や他の形式の解釈なしで文字通りに行われることを意味します。

483
Jo So

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

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

興味があれば説明が続きます。

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

しかし、シェルがコマンドラインからその文字列の配列を作成するとき、シェルはいくつかの文字を特別に解釈します。これは、コマンドを入力しやすくするためのものです。たとえば、スペースは通常、配列内の文字列間の境界を示します。そのため、個々の引数は「単語」と呼ばれることがあります。しかしそれでも引数にはスペースが含まれることがあります。あなたはシェルにあなたが望むものであることを伝えるための何らかの方法が必要です。

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

echo \"Thank\ you.\ \ That\'ll\ be\ \$4.96,\ please,\"\ said\ the\ cashier

...それは面倒になることがあります。そのため、シェルは代わりの方法、つまり引用符を提供しています。これらは主に2つの種類があります。

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

echo "\"Thank you. That'll be \$4.96, please,\" said the cashier"

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

幸い、シェル内の引用符 はWordの区切り文字ではありません ;彼ら自身では、彼らは御言葉を終結させません。同じWord内で、さまざまな種類の引用符の間を含め、引用符の内外に移動して目的の結果を得ることができます。

echo '"Thank you. That'\''ll be $4.96, please," said the cashier'

つまり、バックスラッシュの数が少なくなりますが、クローズシングルクォート、バックスラッシュリテラル、シングルクォート、オープンシングルクォートの順序に慣れることができます。

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

echo $'"Thank you.  That\'ll be $4.96, please," said the cashier'

注意すべき重要なことは、echoコマンドへの引数として受け取られる単一の文字列は まったく同じ これらのすべての例で/ということです。シェルがコマンドラインの解析を終了した後、どのように引用されたのかを伝えるためにコマンドを実行する方法はありません。それがしたかったとしても。

80
Mark Reed

編集: (問題のコメントによると:)

それ以来、私はこれを調べてきました。幸運にも私はレポを置くことができました。それでも、あなたが強制的にあなたのコマンドを一重引用符で囲む必要があるかどうかは私には明らかではない。私はリポジトリの構文を調べましたが、私はあなたが必要とは思わない。コマンドを二重引用符で囲み、二重引用符をエスケープするのであれば、内側に必要な一重引用符と二重引用符を使用することができます。

2
ecv

以下は私のために働いたものです -

QUOTE="'"
Hive -e "alter table TBL_NAME set location $QUOTE$TBL_HDFS_DIR_PATH$QUOTE"
0
Manmohan

ただprintfを使う

の代わりに

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

printfを使用して、変数トークンを展開された変数に置き換えます。

例えば:

template='.... %s'

repo forall -c $(printf "${template}" "${variable}")
0
AndrewD

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

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

repo forall -c $myvar
0
user3734617