web-dev-qa-db-ja.com

コマンド置換と二重引用符:なぜ結果が異なるのですか?

次に、バックティックと$()の動作が異なる例を示します。

$ echo "$(echo \"test\")"
"test"
$ echo "`echo \"test\"`"
test

これは、 "バックスラッシュ内のバックスラッシュ(\)が自明ではない方法で処理される" ためです。

しかし、外側の二重引用符を削除すると結果が同様になったため、これは別のもののようです:

$ echo $(echo \"test\")
"test"
$ echo `echo \"test\"`
"test"

誰かが私にそれがどのように機能するか、そして「 `echo \" test\"`」が二重引用符を削除する理由を説明できますか?

5
Slava Semushin

あなたは正しいです、それはこの場合別のものです。

解決策はまだ 同じリンク にありますが、2番目のポイント:

  • $()内のネストされた引用ははるかに便利です。

    [...]

    `...`を移植可能にするために、内部引用符をバックスラッシュで囲む必要があります。

したがって、

echo "`echo \"test\"`"

これと等しくない:

echo "$(echo \"test\")"

しかしこれは:

echo "$(echo "test")"

代わりにこれと比較する必要があります:

echo "`echo \\"test\\"`"
4
pLumo

その理由は、(二重の)引用符付き文字列がどうなるかです。

シェルパーサーが(引用符で囲まれていない)"を検出するとすぐに、次の(引用符で囲まれていない)"で終わる文字列全体が引用符で囲まれて宣言されます。引用符で囲まれた文字列内では、mostバックスラッシュは削除されませんは削除されません。 UNで引用された文字列では、バックスラッシュが削除されます。

From POSIX(ほとんどのバックスラッシュを削除する)

引用符で囲まれていない<backslash>は、次の文字のリテラル値を保持します...

これは、実際に次のことを示します(set -xは、解析後にシェルが実行する内容を示します)。

$ ksh -c 'set -x; echo test\h\jtest; set +x'
+ echo testhjtest
testhjtest

引用符で囲まれていないバックスラッシュは削除されます。しかし、引用されたものはそうではありません:

$ ksh -c 'set -x; echo "test\h\jtest"; set +x'
+ echo 'test\h\jtest'
test\h\jtest

From POSIX二重引用符

<backslash>は、次のいずれかの文字が続く場合にのみ、エスケープ文字(エスケープ文字(バックスラッシュ)を参照)としての特別な意味を保持します。

それが理由ですバックスラッシュは引用された文字列内で削除されます:

$   `   "   \   <newline>

例:

$ ksh -c 'set -x; echo "test\"\h\j\"test"; set +x'
+ echo 'test"\h\j"test'
test"\h\j"test

これは、最初に見つかった文字が(二重)引用符である場合に起こります。

見つかったものが最初の逆引用符(`)である場合、解析規則は異なります。最初に、文字列は引用符で囲まずに開始され(内部の二重引用符が引用符で囲まれたセクションを開始する場合があります)、引用符のスコープはoneのみです(終了(`)まで)。以下の最初の行の単一の"

$ set -x; echo `echo \"\`echo \\"test\\" \`\"`; set +x
+++ echo '"test"'
++ echo '""test""'
+ echo '""test""'
""test""
+ set +x

代わりに、$(…)new引用スコープを毎回開始します。

$ set -x; echo $(echo \"$(echo \\"test\\" )\"); set +x
+++ echo '\test\'
++ echo '"\test\"'
+ echo '"\test\"'
"\test\"
+ set +x

結果は完全に異なります。

1
Isaac

バッククォートによるコマンド置換が引用を削除する理由は、それが実装されている方法が原因です。

  • バックティック内のテキストは、最終的に実行される前に、もう一度字句パーサーを通過します。

    これは、字句パーサーを最初に渡した結果(引用符レベルを1つ消費する)が文字列として保持され、最後の実行中に完全なパーサーに再び渡されるために発生します。したがって、結果として、バックティックベースのコマンド置換により、2つのレベルの引用が削除されます。

  • テキストwithin$(...)は、最後の実行中に完全なパーサーに渡されるまで変更されません。

    • これは、_')̈́'_と一致する終了__$(_の解析中に、パーサーの副作用として入力テキストを記録することにより、kshに実装されます。

    • これはboshmkshに実装されており、_')̈́'_に一致する終了__$(_を解析して、関連する同等元の入力最終実行のバイナリツリー。

    その結果、$(...)ベースのコマンド置換では、単一の引用レベルのみが削除されます(通常のコマンド実行でも発生するため)。

この実装のため、grepパターンがそのようなコマンドの一部である場合、バックティックベースのコマンド置換により、特別に予期しない動作が発生します。これが$(...)の使用を推奨する理由です。

1
schily