web-dev-qa-db-ja.com

Bashシェルの$ {var}、 "$ var"、および "$ {var}"の違いは何ですか?

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

以下に例を示します。

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

最初の1つだけが、私が望むことを達成します。配列内の各要素を反復処理することです。 $groups"$groups"${groups}、および"${groups}"の違いについてはあまり明確ではありません。誰もがそれを説明できれば、私はそれを感謝します。

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

111
SheerSt

中括弧($var vs. ${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 vs. "$var" vs. "${var}"

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

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

上記の2番目の例の"${var}bar"は、"${var}"barと書くこともできます。この場合、ブレースはもう必要ありません。つまり、"$var"barです。ただし、文字列に引用符がたくさんある場合、これらの代替形式は読みにくくなる可能性があります(したがって、保守が難しくなります)。 このページ は、Bashでの引用の優れた紹介です。

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

さあ、あなたのアレイです。 bash manual によると:

添え字なしで配列変数を参照することは、添え字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[@]} vs. "${foo[@]}"

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

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
196

TL; DR

提供する例はすべて、Bash Shell Expansions のバリエーションです。拡張は特定の順序で行われ、特定の使用例があります。

トークン区切り文字としての中括弧

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

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

配列展開のブレース

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

$ 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

トークン化

質問の残りのほとんどは、引用、およびシェルが入力をトークン化する方法に関するものです。次の例で、シェルの実行方法の違い Word splitting を考慮してください。

$ 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] 1から始まる定位置パラメーターに拡張されます。二重引用符内で展開が行われると、各パラメーターは個別のWordに展開されます。"
  2. 配列では、「[i] fが二重引用符で囲まれている場合、${name[*]}は、IFS変数の最初の文字で区切られた各配列メンバーの値を持つ単一のWordに展開され、${name[@]}名前の各要素を個別のWordに。」

次のように、実際にこれを見ることができます。

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

$ count_params "$@"
3

$ count_params "$*"
1

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

11
Todd A. Jacobs

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

プレーン変数の場合:

  • $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}を参照することは、配列の0番目の要素である${groups[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スクリプトの引数を反復処理する方法 も参照してください。

7

Parameter Expansion in man bashの下の最初の段落の2番目の文は、

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

これは、名前が単にbracesであり、主な目的は名前の始まりと終わりを明確にすることであることを示しています。

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

さらに読むと、あなたは発見します、

パラメーターが複数桁の位置パラメーターである場合、中括弧が必要です…

テストしてみましょう:

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

ほらきちんとした。これを書く前に、私は正直それを知りませんでした(以前に9つ以上の位置パラメータを持ったことがありませんでした)。

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

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

配列の拡張と同様。

3
kojiro

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

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
3
nortally

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

${groups%example}

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

今、あなたのコードを見れば、すべての魔法は中にあります

${groups[@]}

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

特殊文字{}および[]を使用するため、変数を@内に配置しています。変数に名前を付けたり呼び出したりすることはできません:@またはsomething[]は、他の操作と名前のために予約されている文字であるためです。

2
Sierisimo