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


15

を使用していGNU bash 4.3.48ます。単一のドル記号のみが異なる次の2つのコマンドを検討してください。

コマンド1:

echo "(echo " * ")"

コマンド2:

echo "$(echo " * ")"

それらの出力は、それぞれ

(echo  test.txt ppcg.sh )

そして

 * 

したがって、最初のケースで*は明らかにがグロブされます。つまり、最初の引用符は2番目と一緒にペアを形成し、3番目と4番目は別のペアを形成します。

2番目のケースで*はがグロブされず、出力にちょうど2つの余分なスペースがあります。1つはアスタリスクの前、もう1つは後です。つまり、2番目の引用符は3番目の引用符に、最初の引用符は4番目に続きます。

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



繰り返しになりますが、UL SEの構文のハイライトは間違っており、誤解を招く恐れがありますが、JSを非難するのは非難です。
ウェイジュン周

回答:


14

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

これらのコンストラクトにはいくつかのケースがあります。

  • $( ... )あなたが見つけたよう、でのコマンド置換POSIXはこの動作を指定します

    この$(command)フォームでは、開き括弧から対応する閉じ括弧までのすべての文字がコマンドを構成します。コマンドには任意の有効なシェルスクリプトを使用できます...

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

  • を使用したコマンド置換`も。
  • などの高度なパラメータ置換インスタンス${parameter:-word}の「単語」要素。「単語」定義は次のとおりです

    シェルによってユニットとして扱われる一連の文字

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

  • 算術拡大$(( ... ))、それは大部分が役に立たないですが、そこに(ただし、ネストコマンド置換や変数展開は、あまりにも、その後、それらの内、有用引用符を持つことができます)。POSIXの状態

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

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

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

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

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

中かっこが使用される場合、一致する終了中かっこは、バックスラッシュまたは引用符で囲まれた文字列内、および埋め込まれた算術展開、コマンド置換、またはパラメータ展開内ではない最初の '}'です。


一般的なルールとして、すべての区切られたコンストラクトは、周囲のコンテキストとは無関係にボディを解析します(例外はバグとして扱われます)。本質的に、$(コマンド置換コードを見ると、パーサーに新しいプログラムであるかのようにボディからできることを消費するように要求し、サブパーサーが実行されると予想される終了マーカー(エスケープされてい)ない))またはまたは})が表示されることを確認します消費できるものから。

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

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


ありがとう。では"blah/blah\n$(cat "${tmpdir}/${filename}.jpdf")"、なぜ2番目の二重引用符は最初の二重引用符の終わりではなく(返信の構文の強調表示で示されているように)、内部の文字列の始まり$(...)ですか?それは、bashのパーサーがボトムアップではなくトップダウンであるためでしょうか?
すべてのStackExchange

2
の処理には多くのバリエーションがあり"${var-"foo"}"(たとえば、BourneまたはKornシェルecho "${-+"*"}"と同じecho *です)、標準の次のバージョンでは動作が明確に指定されなくなります。また、での議論を参照してくださいmail-archive.com/austin-group-l@opengroup.org/msg00167.html
ステファンChazelas

3

おそらくprintf(ではなくecho)で2つの例を見ると助けになります:

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

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

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

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.