回答:
すべてのリストコンテキストの変数は常に引用符で囲む必要があります。つまり、変数を引用符で囲まずに残す3つの副作用が必要な場合を除き、変数はどこでも複数の値に展開できます。
リストコンテキストでは、単純なようなコマンドに引数を含める[
かecho
、for i in <here>
変数も引用符で囲む必要がある他のコンテキストがあります...アレイに、代入。最善の理由は、そうしない理由がない限り、常に変数を引用することです。
(リストコンテキストで)引用符がないことは、split + glob演算子と考えてください。
かのようecho $test
でしたecho glob(split("$test"))
。
ほとんどの人にとって、シェルの動作は混乱を招きます。他のほとんどの言語でputs("foo")
は、変数(などputs(var)
)ではなくのような固定文字列を引用符で囲みますが、シェルでは逆です:すべてがシェル内の文字列なので、すべてを引用符で囲みます面倒です、あなたecho test
、する必要はありません"echo" "test"
。シェルでは、引用符は他の何かに使用されます。一部の文字の特別な意味を回避したり、一部の展開の動作に影響を与えたりします。
では[ -n $test ]
またはecho $test
、シェルが分割されます$test
(デフォルトでは空白に)した後、ファイル名の生成を行う(すべての拡大*
、「?」...パターンを一致するファイルのリストに)、その後に引数のリストを渡し[
たりecho
するコマンド。
ここでも、と考えます"[" "-n" glob(split("$test")) "]"
。$test
が空の場合、または空白(spc、tab、nl)のみを含む場合、split + glob演算子は空のリストを返します。したがって、[ -n $test ]
は-が"[" "-n" "]"
空の文字列であるかどうかを確認するテストです。しかし、$test
「*」または「= foo」だったらどうなるか想像してみてください...
では[ -n "$test" ]
、[
四つの引数を渡され"["
、"-n"
、""
そして"]"
私たちが望むものである、(引用符なし)。
それはだかどうecho
か、[
違いはありません、それはそれだけだecho
、それが空の引数または全く引数を渡されたかどうか、同じことを出力します。
@ h3rrmillerの答えはのためにあなたが引用符を必要とする理由を説明するための良いですif
(というか、[
/ test
)が、私は実際にあなたの質問が間違っていることを断定う。
次のコマンドを試してみてください、そうすれば私の意味がわかります。
export testvar="123 456"
echo $testvar
echo "$testvar"
引用符がないと、変数置換により2番目のコマンドが次のように展開されます。
echo 123 456
また、複数のスペースは単一のスペースに縮小されます。
echo 123 456
引用符を使用すると、スペースが保持されます。
ので、これは起こりますが(そのパラメータが渡されているかどうかのパラメータを引用する場合echo
、test
または他のいくつかのコマンド)、そのパラメータの値は以下のように送信される1つのコマンドに値。引用符で囲まない場合、シェルは空白を検索するという通常のマジックを実行して、各パラメーターの開始位置と終了位置を決定します。
これは、次の(非常に単純な)Cプログラムでも説明できます。コマンドラインで次のことを試してください(何かを上書きしないように、空のディレクトリで実行することもできます)。
cat <<EOF >paramtest.c
#include <stdio.h>
int main(int argc, char **argv) {
int nparams = argc-1; /* because 1 parameter means only the executable's name */
printf("%d parameters received\n", nparams);
return nparams;
}
EOF
cc -o paramtest paramtest.c
その後...
./paramtest 123 456
./paramtest "123 456"
./paramtest 123 456
./paramtest "123 456"
を実行するとparamtest
、$?
渡されたパラメーターの数が保持されます(その数が出力されます)。
これは、プログラムが実行される前にシェルが行をどのように解釈するかに関するすべてです。
行が読み取るとecho I am $USER
、シェルはそれを拡大echo I am blrfl
し、echo
テキストの起源は、リテラルまたは変数展開であるかどうかを手掛かりを持っていません。同様に、行がの場合echo I am $UNDEFINED
、シェルは$UNDEFINED
何にも展開せず、エコーの引数はになりI am
、これで終わりです。echo
引数なしで正常に動作するため、echo $UNDEFINED
完全に有効です。
あなたの問題はif
本当にではないif
ので、if
どのようなプログラムと引数だけで実行は、それに従うと、実行then
プログラムが終了する場合には一部の0
(またはelse
部分が存在する場合、プログラムは非終了します0
):
if /bin/true ; then echo True dat. ; fi
if fgrep -q blrfl /etc/passwd ; then echo Blrfl has an account. ; fi
if [ ... ]
比較に使用する場合、シェルに組み込まれたプリミティブを使用していません。実際には、最後の引数がを必要[
とする非常にわずかなスーパーセットであるプログラムを実行するようにシェルに指示していtest(1)
ます]
。0
テスト条件が真である1
場合とそうでない場合、両方のプログラムは終了します。
変数が未定義のときに一部のテストが失敗する理由test
は、変数を使用していることがわからないためです。エルゴ、[ $UNDEFINED -eq 2 ]
壊れたのは、シェルがそれで終了するまでに、すべてのtest
引数が-eq 2 ]
であるため、有効なテストではないからです。[ $DEFINED -ne 0 ]
シェルなどの有効なテスト(など0 -ne 0
)に展開するため、のように定義された何かでそれを行った場合は機能します。
にはセマンティックな違いがありますfoo $UNDEFINED bar
。これは、名前に対応しているためfoo
、2つの引数(およびbar
)に展開$UNDEFINED
されます。これfoo "$UNDEFINED" bar
を3つの引数(foo
、空の文字列と `bar)に展開されると比較してください。引用符は、それらの間に何かがあるかどうかに関係なく、シェルがそれらを引数として解釈することを強制します。
引用符$test
がないと複数の単語に展開される可能性があるため、[
コマンド内の各スイッチは引用符が何をするかを1つの引数を期待しているため、構文を壊さないために引用符で囲む必要があります($test
展開されたものはすべて1つの引数になります)
変数を展開するのに引用符が必要ないのecho
は、1つの引数を期待していないためです。それは単にあなたがそれを伝えるものを印刷します。その$test
ため、100ワードに拡張しても、エコーはそれを出力します。
見てみましょうBashの落とし穴
echo
ですか?
echo
。そうでないと思う理由は何ですか?
echo $test
、そしてそれは働く(それは$ testの値を出力する)
引用符で囲まれていない場合、空のパラメーターは削除されます。
start cmd:> strace -e trace=execve echo foo $bar baz
execve("/usr/bin/echo", ["echo", "foo", "baz"], [/* 100 vars */]) = 0
start cmd:> strace -e trace=execve echo foo "$bar" baz
execve("/usr/bin/echo", ["echo", "foo", "", "baz"], [/* 100 vars */]) = 0
呼び出されたコマンドは、シェルコマンドラインに空のパラメーターがあったことを認識しません。[は、-nに対して0を返すように定義されているようです。とにかく。
引用もいくつかの場合にエコーに違いをもたらします:
var='*'
echo $var
echo "$var"
var="foo bar"
echo $var
echo "$var"
echo
ではなく、シェルです。同じ動作が表示されls
ます。touch '*'
冒険心があると感じたら、しばらく試してください。:)
echo
、余分なスペースと改行が削除されます。