web-dev-qa-db-ja.com

bashの二重引用符はどのように一致(ペア)されますか?

_GNU bash_ _4.3.48_を使用しています。ドル記号が1つだけ異なる次の2つのコマンドを検討してください。

コマンド1:

_echo "(echo " * ")"
_

コマンド2:

_echo "$(echo " * ")"
_

それらの出力はそれぞれです

_(echo  test.txt ppcg.sh )
_

そして

_ * 
_

したがって、最初のケースでは明らかに_*_がグロブ化されます。これは、最初の引用符が2番目の引用符と組み合わせてペアを形成し、3番目と4番目の引用符が別のペアを形成することを意味します。

2番目のケースでは、_*_はグロブ化されておらず、出力には2つの余分なスペースがあり、1つはアスタリスクの前、1つは後です。第4。

$()構成以外に、引用符が次の引用符と一致せず、代わりにネストされるケースはありますか?この動作は十分に文書化されていますか?ある場合、対応する文書はどこにありますか?

15
Weijun Zhou

文字列内で補間できる入れ子構造は、その中にさらに文字列を含めることができます。これらは、新しいスクリプトのように、終了マーカーまで解析され、さらに複数のレベルでネストすることもできます。それらの1つすべてのバーは$で始まります。それらのすべては、BashマニュアルとPOSIXシェルコマンド言語仕様の組み合わせで文書化されています。

これらの構成にはいくつかのケースがあります。

  • $( ... ) を使用したコマンド置換。 POSIXはこの動作を指定しています

    $(command)形式では、左括弧に続く対応する右括弧までのすべての文字がコマンドを構成します。 command ...には、任意の有効なシェルスクリプトを使用できます。

    引用符は有効なシェルスクリプトの一部であるため、通常の意味で使用できます。

  • `を使用したコマンド置換。
  • ${parameter:-Word}などの高度なパラメータ置換インスタンス の「Word」要素。 "Word"の定義は

    シェルによって1つの単位として扱われる一連の文字

    -引用符付きテキストと混合引用符a"b"c'd'eを含みます-ただし、展開の実際の動作はそれよりも少し自由で、たとえば${x:-hello world}も機能します。

  • 算術展開$(( ... ))を併用しますが、ほとんど役に立ちません(ただし、コマンド置換や変数展開をネストして、その中に引用符を含めることもできます)。 POSIXによると

    式は、二重引用符内にあるかのように扱われます。ただし、式内の二重引用符は特別に扱われません。シェルは、パラメーター展開、コマンド置換、引用削除のために、式内のすべてのトークンを展開します。

    したがって、この動作は明示的に必要です。つまり、echo "abc $((4 "*" 5))"は、グロビングではなく、算術演算を行います。

    ただし、古いスタイルの$[ ... ]算術展開はnotと同じように扱われることに注意してください。展開が引用されているかどうかに関係なく、引用符が表示されるとエラーになります。このフォームはもはや文書化されておらず、とにかく使用するためのものではありません。

  • $"..."を使用したロケール固有の変換 。これは、実際に"をコア要素として使用します。 $"は単一のユニットとして扱われます。

ブレース展開 を使用した、引用符を含まない、予期しないネストのケースがもう1つあります。{a,b{c,d},e}は "a bc bd e"に展開されます。 ${x:-a{b,c}d}notネストしますが、これは、「a{b,c」の後に「d}」が続くパラメーター置換として扱われます。その も文書化されています

中かっこを使用する場合、一致する終了中かっこは、バックスラッシュまたは引用符付き文字列内でエスケープされていない最初の ‘}’であり、埋め込まれた算術展開、コマンド置換、またはパラメーター展開内ではありません。


一般的な規則として、すべての区切られた構成は、周囲のコンテキストとは無関係にその本体を解析します(および 例外はバグとして扱われます )。本質的に、$(を確認すると、コマンド置換コードはパーサーに新しいプログラムであるかのように本体から可能なものを消費するように要求し、予期される終了マーカー(エスケープされていない)または))または})が表示されることを確認します。サブパーサーは、消費できるものを使い果たします。

再帰下降パーサー の機能について考えると、それは基本ケースへの単純な再帰です。文字列の補間が完了したら、実際には他の方法よりも簡単です。基礎となる解析手法に関係なく、これらの構成をサポートするシェルは同じ結果をもたらします。

これらの構成要素を使用して好きなだけ深く引用をネストでき、期待どおりに機能します。途中で引用を見て混乱することはありません。代わりに、それが内部コンテキストでの新しい引用符付き文字列の始まりになります。

14
Michael Homer

おそらく、printfの代わりにechoを使用して2つの例を確認すると、次のように役立ちます。

$ printf '<%s> ' "(echo " * ")"; echo
<(echo > <test.txt> <ppcg.sh> <file1> <file2> <file3> <)>

(echo (末尾のスペースを含む最初のWord)、いくつかのファイル、および最後のWord )を出力します。
括弧は引用された文字列(echo の一部です。
アスタリスク(2つの二重引用符がペアになっているため、現在は引用符で囲まれていません)は、一致するファイルのリストにグロブとして展開されます。
そして、閉じ括弧。

ただし、2番目のコマンドは次のように機能します。

$ printf '<%s> ' "$(echo " * ")" ; echo
< * >

$は、コマンド置換を開始します。それは引用を新たに始める。
アスタリスクは" * "で囲まれており、それがコマンド(ここではコマンドであり、引用符で囲まれた文字列ではありません)echoが出力するものです。最後に、printf*を再フォーマットし、< * >として出力します。

3
Isaac