
変数を拡張するには二重引用符が必要であることを読みました。
if [ -n "$test" ]; then echo '$test ok'; else echo '$test null'; fi
期待どおりに動作しますが、
if [ -n $test ]; then echo '$test ok'; else echo '$test null'; fi
$test ok
nullの場合でも常に呼び出されます$test
。
しかし、なぜ引用符が必要ないのですかecho $test
?
答え1
すべての変数には常に引用符が必要です。リスト言い換えれば、変数を引用しないままにするという3つの副作用を実際に望まない限り、変数はどこからでも複数の値に拡張できます。
リストコンテキストには、[
またはecho
、for i in <here>
配列への割り当てなどの単純なコマンドのパラメータが含まれます。他のコンテキストもあり、変数も参照する必要があります。妥当な理由がない限り、常に変数を引用するのが最善です。
考えると引用符なし(リストコンテキストで)次のように分割+グローバルオペレーター。
まるでecho $test
。echo glob(split("$test"))
puts("foo")
他のほとんどの言語では、変数(たとえば)の周りではなく、固定文字列の周りに引用符を付けますが、puts(var)
シェルではその逆です。つまり、シェルのすべてはすべて文字列です。したがって、すべてに引用符を付けるのは面倒であり、あなたはecho test
それを必要としません"echo" "test"
。シェルでは、引用符は他の目的、つまり特定の文字の特別な意味を回避したり、特定の拡張子の動作に影響を与えるために使用されます。
[ -n $test ]
orでは、echo $test
シェルは分割$test
(デフォルトでは空白)を実行し、ファイル名の生成(すべての「*
?」...パターンを一致するファイルのリストに拡張)し、その引数のリストを[
orecho
コマンドに渡します。
もう一度考えてみてください"[" "-n" glob(split("$test")) "]"
。空白または空白(spc、tab、nl)のみを含む場合、$test
分割+glob演算子は空のリストを返すため、「-n」が空の文字列であることを確認するテスト[ -n $test ]
です。"[" "-n" "]"
しかし、$test
「*」または「= foo」なら、何が起こるのか想像してみてください。
、および(引用符なし)には4つのパラメータが渡されます。[ -n "$test" ]
これは私たちが望むものです。[
"["
"-n"
""
"]"
echo
渡すかどうかは[
違いはありませんecho
。空のパラメータを渡してもパラメータをまったく渡さなくても、同じ内容が出力される点だけが異なります。
また、見ることができますこの同様の質問に対する答え[
コマンドと設定の[[...]]
詳細。
答え2
@h3rrmillerの返信if
引用符(またはむしろ[
/)が必要な理由を説明するのに役立ちますが、test
実際にはあなたの質問が間違っているようです。
次のコマンドを試してみると、どういう意味かがわかります。
export testvar="123 456"
echo $testvar
echo "$testvar"
引用符がない場合、変数の代入により、2 番目のコマンドが次に展開されます。
echo 123 456
そして、いくつかのスペースが一つに縮小されます。
echo 123 456
引用符を使用すると、スペースが保持されます。
このようなことが起こる理由はパラメータを引用すると(パラメータがに渡されるか別のコマンドに渡されるかecho
)、test
パラメータの値は次のように渡されます。一つコマンドの値です。引用しない場合、シェルは各引数が始まり終わる位置を決定するために空白を見つける一般的な魔法を実行します。
これは、次の(非常に、非常に単純な)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
を実行すると、渡された$?
引数の数が保存されます(そしてその数が印刷されます)。
答え3
これがシェルが行の内容全体を解釈する方法です。今後プログラムが実行されます。
行が読み込まれると、echo I am $USER
シェルはそれをに展開echo I am blrfl
し、echo
テキストソースがリテラルなのか変数の拡張なのかを知ることはできません。同様に、行にが含まれている場合、echo I am $UNDEFINED
シェルは$UNDEFINED
何も拡張されず、echoの引数はになり、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
。
間には意味の違いがあり、名前が示すように2つのパラメータ(および)foo $UNDEFINED bar
に拡張されます。と比較すると、次のように拡張されます。foo
bar
$UNDEFINED
foo "$UNDEFINED" bar
サムパラメータ(foo
、空の文字列、 `bar)。引用符は、シェルが引用符の間に内容があるかどうかに関係なく、引用符を引数として解釈します。
答え4
引用符がない場合は、空のパラメータが削除されます。
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"