Bashシェルの$ {var}、「$ var」、「$ {var}」の違いは何ですか?


133

タイトルの意味:変数をカプセル化するとはどういう意味ですか {}""または"{}私はこのことについて、オンライン任意の説明を見つけることができませんでした「 -私は、シンボルを使用したこと以外はそれらを参照することができていない、います?何も生み出しません。

次に例を示します。

declare -a groups

groups+=("CN=exampleexample,OU=exampleexample,OU=exampleexample,DC=example,DC=com")
groups+=("CN=example example,OU=example example,OU=example example,DC=example,DC=com")

この:

for group in "${groups[@]}"; do
    echo $group
done

これとは大きく異なることが証明されています。

for group in $groups; do
    echo $group
done

この:

for group in ${groups}; do
    echo $group
done

最初のものだけが私が望むことを達成します:配列の各要素を反復します。との違いはよくわかりませんが$groups"$groups"${groups}"${groups}"。どなたかご説明頂ければ幸いです。

追加の質問として-これらのカプセル化を参照するための受け入れられた方法を誰かが知っていますか?


回答:


228

ブレース($varvs. ${var}

ほとんどのケースでは、$var${var}同じです。

var=foo
echo $var
# foo
echo ${var}
# foo

ブレースは、式のあいまいさを解決するためにのみ必要です。

var=foo
echo $varbar
# Prints nothing because there is no variable 'varbar'
echo ${var}bar
# foobar

引用符($var"$var""${var}"

変数を二重引用符で囲むと、空白が含まれている場合でも、変数を単一の単語として扱うようにシェルに指示します。

var="foo bar"
for i in "$var"; do # Expands to 'for i in "foo bar"; do...'
    echo $i         #   so only runs the loop once
done
# foo bar

この動作を以下と比較してください。

var="foo bar"
for i in $var; do # Expands to 'for i in foo bar; do...'
    echo $i       #   so runs the loop twice, once for each argument
done
# foo
# bar

同様に$var${var}、括弧だけ例えば、曖昧さ回避のために必要とされます。

var="foo bar"
for i in "$varbar"; do # Expands to 'for i in ""; do...' since there is no
    echo $i            #   variable named 'varbar', so loop runs once and
done                   #   prints nothing (actually "")

var="foo bar"
for i in "${var}bar"; do # Expands to 'for i in "foo barbar"; do...'
    echo $i              #   so runs the loop once
done
# foo barbar

"${var}bar"上記の2番目の例ではも記述できることに注意してください。その"${var}"bar場合、中括弧はもう必要ありません"$var"bar。ただし、文字列に多くの引用符がある場合、これらの代替形式は読みにくくなる可能性があります(そのため、維持が困難になります)。このページでは、Bashでの引用の概要を説明しています。

配列($varvs. $var[@]vs. ${var[@]}

今あなたの配列のために。bashのマニュアルによると:

添え字なしで配列変数を参照することは、添え字0で配列を参照することと同じです。

つまり、でインデックスを指定しない場合[]、配列の最初の要素が取得されます。

foo=(a b c)
echo $foo
# a

これはまったく同じです

foo=(a b c)
echo ${foo}
# a

配列のすべての要素を取得するに@は、インデックスなどとして使用する必要があります${foo[@]}。配列では中括弧が必要です。それがないと、シェルが$foo最初にパーツを展開し、配列の最初の要素にリテラルが続くから[@]です。

foo=(a b c)
echo ${foo[@]}
# a b c
echo $foo[@]
# a[@]

このページは、Bashの配列の優れた紹介です。

引用の再訪(${foo[@]}"${foo[@]}"

あなたはこれについて尋ねませんでしたが、それは知っておくと良い微妙な違いです。配列の要素に空白が含まれる可能性がある場合は、二重引用符を使用して、各要素が個別の「単語」として扱われるようにする必要があります。

foo=("the first" "the second")
for i in "${foo[@]}"; do # Expands to 'for i in "the first" "the second"; do...'
    echo $i              #   so the loop runs twice
done
# the first
# the second

これを二重引用符なしの動作と比較してください。

foo=("the first" "the second")
for i in ${foo[@]}; do # Expands to 'for i in the first the second; do...'
    echo $i            #   so the loop runs four times!
done
# the
# first
# the
# second

3
別のケースがあります:${var:?}、変数が設定されていないか設定されていない場合にエラーが発生します。REF:github.com/koalaman/shellcheck/wiki/SC2154
Nam Nguyen

4
あなたは他の形態について話をしたい場合は@NamNguyen パラメータ展開:、より少なくともダースがある${parameter:-word}${parameter:=word}${parameter#word}${parameter/pattern/string}、など。これらはこの回答の範囲を超えていると思います。
ThisSuitIsBlackNot

実際、二重引用符の説明は不完全です。詳細については、stackoverflow.com
questions / 10067266 /…を

11

TL; DR

あなたが与えるすべての例は、Bash Shell Expansionsのバリエーションです。拡張は特定の順序で行われ、一部には特定の使用例があります。

トークン区切り文字としてのブレース

この${var}構文は主に、あいまいなトークンを区切るために使用されます。たとえば、次のことを考慮してください。

$ var1=foo; var2=bar; var12=12
$ echo $var12
12
$ echo ${var1}2
foo2

配列展開の括弧

中括弧は、配列の要素にアクセスするため、およびその他の特別な展開のために必要です。例えば:

$ foo=(1 2 3)

# Returns first element only.
$ echo $foo
1

# Returns all array elements.
$ echo ${foo[*]}
1 2 3

# Returns number of elements in array.
$ echo ${#foo[*]}
3

トークン化

残りの質問のほとんどは引用と、シェルが入力をトークン化する方法に関係しています。次の例で、シェルが単語分割を実行する方法の違いを考慮してください。

$ var1=foo; var2=bar; count_params () { echo $#; }

# Variables are interpolated into a single string.
$ count_params "$var1 $var2"
1

# Each variable is quoted separately, created two arguments.
$ count_params "$var1" "$var2"
2

@記号はとは異なる引用と対話します*。具体的には:

  1. $@ 「[e] xpandsは位置パラメーターから始まり、1から始まります。二重引用符内で展開が行われると、各パラメーターは個別の単語に展開されます。」
  2. 配列では、「単語が二重引用符で囲まれ、${name[*]}IFS変数の最初の文字で区切られた各配列メンバーの値で単一の単語に展開され、${name[@]}nameの各要素が個別の単語に展開されます。」

これは、次のように動作していることがわかります。

$ count_params () { echo $#; }
$ set -- foo bar baz 

$ count_params "$@"
3

$ count_params "$*"
1

シェルが意図したとおりに単語分割できない可能性があるスペースまたは特殊文字を含む変数を変数が参照する場合、引用符で囲まれた展開の使用は非常に重要です。Bashでの引用の仕組みの詳細については、引用を参照てください。


7

配列と単純な変数を区別する必要があります—例では配列を使用しています。

プレーン変数の場合:

  • $var${var}まったく同じです。
  • "$var""${var}"まったく同じです。

ただし、2つのペアはすべてのケースで100%同一というわけではありません。以下の出力を検討してください。

$ var="  abc  def  "
$ printf "X%sX\n" $var
XabcX
XdefX
$ printf "X%sX\n" "${var}"
X  abc  def  X
$

変数を二重引用符で囲まないと、内部の間隔が失われ、展開はprintfコマンドの2つの引数として扱われます。変数を二重引用符で囲むと、内部の間隔が保持され、展開はprintfコマンドへの1つの引数として扱われます。

配列の場合、ルールは似ていますが異なります。

  • groupsが配列の場合、参照$groupsまたは${groups}と同じ${groups[0]}です。配列の0番目の要素。
  • 参照 "${groups[@]}"はと似ています"$@"。配列の個々の要素の間隔を保持し、配列の要素ごとに1つの値の値のリストを返します。
  • ${groups[@]}二重引用符なしで参照すると、スペースが保持されず、要素にスペースが含まれている場合、配列の要素よりも多くの値が導入される可能性があります。

例えば:

$ groups=("abc def" "  pqr  xyz  ")
$ printf "X%sX\n" ${groups[@]}
XabcX
XdefX
XpqrX
XxyzX
$ printf "X%sX\n" "${groups[@]}"
Xabc defX
X  pqr  xyz  X
$ printf "X%sX\n" $groups
XabcX
XdefX
$ printf "X%sX\n" "$groups"
Xabc defX
$

*代わりに使用@、微妙に異なる結果が得られます。

スクリプト内の引数を反復する方法bashも参照してください。


3

下の最初の段落の2番目の文パラメータ展開man bash言います、

展開されるパラメータ名またはシンボルは中括弧で囲むことができます。これらはオプションですが、名前の一部として解釈される可能性のある直後の文字から展開される変数を保護する役割を果たします。

これは、名前が中かっこであることがわかり、主な目的は、名前の始まりと終わりの場所を明確にすることです。

foo='bar'
echo "$foobar"
# nothing
echo "${foo}bar"
barbar

さらに読んだ場合は、

中かっこは、parameterが複数桁の位置パラメータの場合に必要です…

テストしてみましょう:

$ set -- {0..100}
$ echo $22
12
$ echo ${22}
20

ええと。きちんと。私はこれを書く前に正直にそれを知りませんでした(これまでに9個を超える定位置パラメーターはありませんでした)。

もちろん、次のような強力なパラメータ拡張機能を実行するには中括弧も必要です。

${parameter:-word}
${parameter:=word}
${parameter:?word}
 [read the section for more]

アレイの拡張と同様に。


3

上記でカバーされていない関連ケース。空の変数を引用すると、の状況が変わるようですtest -n。これは特にのinfoテキストの例として示されていますがcoreutils、実際には説明されていません。

16.3.4 String tests
-------------------

These options test string characteristics.  You may need to quote
STRING arguments for the shell.  For example:

     test -n "$V"

  The quotes here prevent the wrong arguments from being passed to
`test' if `$V' is empty or contains special characters.

詳細な説明を聞きたいです。私のテストはこれを確認し、同じ結果-z-n返さないようにするために、すべての文字列テストの変数を引用しています。

$ unset a
$ if [ -z $a ]; then echo unset; else echo set; fi
unset
$ if [ -n $a ]; then echo set; else echo unset; fi    
set                                                   # highly unexpected!

$ unset a
$ if [ -z "$a" ]; then echo unset; else echo set; fi
unset
$ if [ -n "$a" ]; then echo set; else echo unset; fi
unset                                                 # much better

2

まあ、私は変数のカプセル化が次のようなものを扱うのに役立つことを知っています:

${groups%example}

または、そのような構文で、値を返す前に変数で何かを実行したい場合。

今、あなたのコードを見ると、すべての魔法が中にあります

${groups[@]}

魔法はそこにあります:あなたはただ書くことができないので: $groups[@]

{}特殊文字[]and を使用したいので、変数を内に配置しています@。:名前またはちょうどあなたの変数を呼び出すことはできません@か、something[]これらは他の操作と名の予約文字であるため。


これは、二重引用符の非常に重要な意味、およびそれらを含まないコードが基本的にどのように壊れているかを指摘することに失敗しています。
tripleee 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.