GNU/Linux >> Linux の 問題 >  >> Linux

二重引用符はいつ必要ですか?

以前のアドバイスは、$VARIABLEを含む式を二重引用符で囲むことでした。 、少なくともシェルによって1つのアイテムとして解釈されるようにしたい場合は、それ以外の場合は、$VARIABLEのコンテンツ内のスペース シェルを捨てるでしょう。

ただし、最近のバージョンのシェルでは、(少なくとも上記の目的では)二重引用符が常に必要になるとは限らないことを理解しています。たとえば、bash

% FOO='bar baz'
% [ $FOO = 'bar baz' ] && echo OK
bash: [: too many arguments
% [[ $FOO = 'bar baz' ]] && echo OK
OK
% touch 'bar baz'
% ls $FOO
ls: cannot access bar: No such file or directory
ls: cannot access baz: No such file or directory

zshで 一方、同じ3つのコマンドは成功します。したがって、この実験に基づくと、bash[[ ... ]]内の二重引用符は省略できます 、ただし[ ... ]内にはありません コマンドライン引数でも、zshでは 、これらすべての場合で二重引用符は省略できます。

しかし、上記のような逸話的な例から一般的なルールを推測することは、偶然の命題です。二重引用符が必要な場合の概要を確認すると便利です。私は主にzshに興味があります 、bash 、および/bin/sh

承認された回答:

まず、zshを残りの部分から分離します。古いシェルと最新のシェルの問題ではありません。zshの動作は異なります。 zshの設計者は、従来のシェル(Bourne、ksh、bash)と互換性を持たないようにすることを決定しましたが、使いやすくなっています。

第二に、いつ必要になるかを覚えておくよりも、常に二重引用符を使用する方がはるかに簡単です。ほとんどの場合必要なので、必要なときではなく、必要のないときに学ぶ必要があります。

一言で言えば、単語のリストまたはパターンが予想される場合は常に二重引用符が必要です 。これらは、パーサーが生の文字列を期待するコンテキストではオプションです。

引用符なしで何が起こるか

二重引用符がないと、2つのことが起こることに注意してください。

  1. 最初に、展開の結果(${foo}のようなパラメーター置換の変数の値> 、または$(foo)のようなコマンド置換のコマンドの出力 )は、空白が含まれている場合は常に単語に分割されます。
    より正確には、展開の結果は、IFSの値に表示される各文字で分割されます。 変数(区切り文字)。区切り文字のシーケンスに空白(スペース、タブ、または改行)が含まれている場合、空白は1文字としてカウントされます。先頭、末尾、または繰り返しの非空白区切り文字は、空のフィールドにつながります。たとえば、IFS=" :":one::two : three: :four  oneの前に空のフィールドを生成します 、oneの間 およびtwo 、および(1つ)three およびfour
  2. 分割の結果として生じる各フィールドは、[*?の文字のいずれかが含まれている場合、グロブ(ワイルドカードパターン)として解釈されます。 。そのパターンが1つ以上のファイル名と一致する場合、そのパターンは一致するファイル名のリストに置き換えられます。

引用符で囲まれていない変数展開$foo "$foo"とは対照的に、口語的には「split+glob演算子」として知られています。 これは、変数fooの値を取得するだけです。 。同じことがコマンド置換にも当てはまります:"$(foo)" コマンド置換、$(foo) コマンド置換の後にsplit+globが続きます。

二重引用符を省略できる場合

これが、二重引用符なしで変数またはコマンド置換を記述でき、値が文字通りに解釈されるボーンスタイルのシェルで考えられるすべてのケースです。

  • 割り当ての右側。

    var=$stuff
    a_single_star=*
    

    exportの後に二重引用符が必要であることに注意してください 、これは通常の組み込みであり、キーワードではないためです。これは、dash、zsh(shエミュレーション)、yash、poshなどの一部のシェルにのみ当てはまります。 bashとkshはどちらもexportを扱います 特別に。

    export VAR="$stuff"
    
  • caseの場合 ステートメント。

    case $var in …
    

    ケースパターンでは二重引用符が必要であることに注意してください。単語の分割は大文字小文字のパターンでは発生しませんが、引用符で囲まれていない変数はパターンとして解釈され、引用符で囲まれた変数はリテラル文字列として解釈されます。

    a_star='a*'
    case $var in
      "$a_star") echo "'$var' is the two characters a, *";;
       $a_star) echo "'$var' begins with a";;
    esac
    
  • 二重括弧内。二重角かっこはシェルの特殊な構文です。

    [[ -e $filename ]]
    

    パターンまたは正規表現が必要な場合は、二重引用符が必要です。=の右側 または== または!= または=~

    a_star='a*'
    if [[ $var == "$a_star" ]]; then echo "'$var' is the two characters a, *"
    elif [[ $var == $a_star ]]; then echo "'$var' begins with a"
    fi
    

    通常どおり、角かっこで囲まれた二重引用符が必要です[ … ] これは通常のシェル構文であるためです(これはたまたま[と呼ばれるコマンドです )。シングルブラケットまたはダブルブラケットを参照してください

  • 非対話型POSIXシェル(bashではない)でのリダイレクト 、またはksh88

    echo "hello world" >$filename
    

    一部のシェルは、対話型の場合、変数の値をワイルドカードパターンとして扱います。 POSIXは、非対話型シェルでのその動作を禁止していますが、bash(POSIXモードを除く)およびksh88((おそらく)POSIX shとして検出された場合を含む)を含むいくつかのシェル Solarisのようないくつかの商用ユニスの)まだそこでそれを行います(bash 分割も試みます split + globbing でない限り、リダイレクトは失敗します 結果は正確に1語になります)。そのため、リダイレクトのターゲットをshで引用することをお勧めします。 bashに変換する場合のスクリプト いつかスクリプトを作成するか、shがあるシステムで実行します その点で非準拠であるか、ソースである可能性があります インタラクティブシェルから。

  • 算術式の内部。実際、変数を算術式として解析するには、引用符を省略する必要があります。

    expr=2*2
    echo "$(($expr))"
    

    ただし、POSIXが要求するように、ほとんどのシェルで単語分割の対象となるため、算術展開の前後に引用符が必要です(!?)。

  • 連想配列の添え字。

    typeset -A a
    i='foo bar*qux'
    a[foo bar*qux]=hello
    echo "${a[$i]}"
    

引用符で囲まれていない変数とコマンド置換は、まれな状況で役立つ場合があります。

  • 変数値またはコマンド出力がグロブパターンのリストで構成されており、これらのパターンを一致するファイルのリストに拡張する場合。
  • 値にワイルドカード文字が含まれていないことがわかっている場合は、その$IFS 変更されておらず、空白文字で分割したい。
  • 特定の文字で値を分割する場合:set -fを使用してグロビングを無効にします 、IFSを設定します 区切り文字に変更して(または空白で分割するためにそのままにして)、展開を実行します。
関連:RAMに十分な空き容量があるのに、なぜスワップを使用するのですか?

Zsh

zshでは、いくつかの例外を除いて、ほとんどの場合、二重引用符を省略できます。

  • $var varの値が次の場合、複数の単語に展開されることはありませんが、(単一の空の単語を含むリストではなく)空のリストに展開されます。 空の文字列です。コントラスト:

    var=
    print -l $var foo        # prints just foo
    print -l "$var" foo      # prints an empty line, then foo
    

    同様に、"${array[@]}" $arrayが配列のすべての要素に展開され、 空でない要素にのみ展開されます。

  • @ パラメータ展開フラグでは、置換全体を二重引用符で囲む必要がある場合があります:"${(@)foo}"

  • 引用符で囲まれていない場合、コマンド置換はフィールド分割されます:echo $(echo 'a'; echo '*') a *を出力します (単一のスペースで)一方、echo "$(echo 'a'; echo '*')" 変更されていない2行の文字列を出力します。 "$(somecommand)"を使用します コマンドの出力を一言で取得するには、最後の改行はありません。 "${$(somecommand; echo _)%?}"を使用します 最終的な改行を含むコマンドの正確な出力を取得します。 "${(@f)$(somecommand)}"を使用します コマンドの出力から行の配列を取得します。

関連:タイプセット-Aはスクリプトでエラーを出しますか?
Linux
  1. チルダ(〜)が二重引用符の内側に展開されないのはなぜですか?

  2. 引用の中のワイルドカード?

  3. 別のコマンドの単一引用符と二重引用符を含むコマンドをラップしますか?

  1. Nohupをいつ使用するのですか?

  2. 引用符を使用すると、なぜ単一の円記号が表示されるのですか?

  3. bash / 標準の Linux コマンドのみを使用して、文字列内の一重引用符と二重引用符を削除する

  1. シェル変数を引用符で囲むのはいつですか?

  2. AWK スクリプト出力のフィールドを二重引用符で囲みますか?

  3. IFS を改行で分割するように設定する場合、バックスペースを含める必要があるのはなぜですか?