変数を参照する必要がありますが、echoが参照しないのはなぜですか?

変数を参照する必要がありますが、echoが参照しないのはなぜですか?

変数を拡張するには二重引用符が必要であることを読みました。

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 oknullの場合でも常に呼び出されます$test

しかし、なぜ引用符が必要ないのですかecho $test

答え1

すべての変数には常に引用符が必要です。リスト言い換えれば、変数を引用しないままにするという3つの副作用を実際に望まない限り、変数はどこからでも複数の値に拡張できます。

リストコンテキストには、[またはechofor i in <here>配列への割り当てなどの単純なコマンドのパラメータが含まれます。他のコンテキストもあり、変数も参照する必要があります。妥当な理由がない限り、常に変数を引用するのが最善です。

考えると引用符なし(リストコンテキストで)次のように分割+グローバルオペレーター。

まるでecho $testecho 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に拡張されます。と比較すると、次のように拡張されます。foobar$UNDEFINEDfoo "$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"

関連情報