web-dev-qa-db-ja.com

引数から読み取ったコマンドを使用してsh(ダッシュ)からbashを呼び出し、「引用符で囲まれていない文字列」/「予期しないEOF」

これを文書化すると思っただけです。非常に単純なことを試みています。bashに環境変数を設定し、それを出力します。

_$ bash -c "a=1; echo a$a;"
a
$ bash -c "a=1; echo a\$a;"
a1
_

今、私はこれと同じことを望みますが、shの引数として呼び出されます(私のシステムでは、ls -la $(which sh)は_/bin/sh -> dash_を与えます):

_$ sh -c "bash -c "a=1; echo a\$a;""
a$a

# obviously I have to escape inner quotes:

$ sh -c "bash -c \"a=1; echo a\$a;\""
a

# escape the dollar once more?

$ sh -c "bash -c \"a=1; echo a\\$a\" "
sh: Syntax error: Unterminated quoted string

# nope... inner single quotes, then?

$ sh -c "bash -c 'a=1; echo a$a;'"
a

# nope... escape the single quotes?

$ sh -c "bash -c \'a=1; echo a$a;\'"
bash: -c: line 0: unexpected EOF while looking for matching `''
bash: -c: line 1: syntax error: unexpected end of file
a
sh: ': not found

# nope... escape the dollar too?

$ sh -c "bash -c \'a=1; echo a\$a;\'"
bash: -c: line 0: unexpected EOF while looking for matching `''
bash: -c: line 1: syntax error: unexpected end of file
a
sh: ': not found
_

だから、私の質問は-_sh -c [bash -c ...]_がちょうど_bash -c ..._と同じ結果を与えるように、エスケープするための適切な構文は何ですか?

5
sdaau

一重引用符の中には、特別な意味を持つ文字はありません。二重引用符で囲まれた"\$`には特別な意味があります。外側から内側に向​​かって作業します。最初に外側のシェルによって構築される文字列を把握し、次に内側のシェルがそれをどのように作成するかを把握します。

たとえば、変数aが外部シェルで定義されていないと仮定します。

sh -c "bash -c \"a=1; echo a\$a;\""

外側のシェルは、bash -c "a=1; echo a$a;"に展開される二重引用符で囲まれた文字列を認識し、中間のshに渡されるのはその文字列です。 shは、これを2つの引数-ca=1; echo a;を使用したbashの呼び出しとして解析します。後者は、二重引用符で囲まれた文字列"a=1; echo a$a;"を展開した結果です。変数aが定義されていません。

この分析を行うと、必要なものを取得する1つの方法は、この段階で"a=1; echo a\$a;"を使用することであることがわかります。この余分な円記号を取得するには、元の円記号に2つの円記号を入れる必要があります。これは、二重引用符で囲まれたシェル拡張の1つの段階がすでに存在するためです。 2プラス1は3です。

sh -c "bash -c \"a=1; echo a\\\$a;\""

外側の文字列には何も展開したくないので、外側の文字列には一重引用符を使用する方が簡単です。

sh -c 'bash -c "a=1; echo a\$a;"'

bashからshを呼び出すときにも何も展開したくないので、そこで一重引用符を使用することもできます。一重引用符で囲まれた文字列内に一重引用符を入れることはできませんが、これをシミュレートする方法があります:put '\''。正式には、これは一重引用符付きリテラルを終了し、リテラル一重引用符を追加し、新しい一重引用符付きリテラルを開始しますが、4文字のシーケンス'\''は、一重引用符を内に置く方法と考えることができます。一重引用符で囲まれた文字列。

sh -c 'bash -c '\''a=1; echo a$a;'\'''

最後に''を省略できます。体系的ではありませんが、目には簡単です。

sh -c 'bash -c '\''a=1; echo a$a;'\'

一重引用符は二重引用符内で特別ではないため、"bash -c 'a=1; echo a\$a;'"と書くときは、外側のシェルで$aが拡大しないように、ドルの前に円記号が必要です。

さて、私はうまくいくカップルを見つけました(NB、これはUbuntu 11.04です):

$ sh -c "bash -c 'a=1; echo a\$a \'"
a1
$ sh -c "bash -c 'a=1; echo a\$a;'"
a1

...そして、最初の一重引用符がエスケープされておらず、ドルがエスケープされている限り、最後の一重引用符がエスケープされているかどうかに関係なく機能するようです!それはまだ私をかなり困惑させます...

1
sdaau