読んだ後Bash マニュアルページそしてに関してこの郵便受けeval
、コマンドが具体的に何を実行し、その典型的な用途は何かを理解するのにまだ苦労しています。
たとえば、次のようにします。
$ set -- one two three # Sets $1 $2 $3
$ echo $1
one
$ n=1
$ echo ${$n} ## First attempt to echo $1 using brackets fails
bash: ${$n}: bad substitution
$ echo $($n) ## Second attempt to echo $1 using parentheses fails
bash: 1: command not found
$ eval echo \${$n} ## Third attempt to echo $1 using 'eval' succeeds
one
ここでは正確に何が起こっているのでしょうか。また、ドル記号とバックスラッシュは問題とどのように関係しているのでしょうか。
ベストアンサー1
eval
文字列を引数として受け取り、その文字列をコマンドラインに入力したかのように評価します。(複数の引数を渡す場合、最初にそれらはスペースで結合されます。)
${$n}
これは bash の構文エラーです。中括弧内には、いくつかのプレフィックスとサフィックスが付いた変数名のみを記述できますが、任意の bash 構文を使用することはできず、特に変数の展開は使用できません。ただし、「この変数に名前がある変数の値」を記述する方法はあります。
echo ${!n}
one
$(…)
は、括弧内に指定されたコマンドをサブシェル(つまり、現在のシェルから変数値などのすべての設定を継承する別のプロセス)で実行し、その出力を収集します。 はシェルコマンドとしてecho $($n)
実行され$n
、その出力を表示します。$n
は と評価されるため1
、存在しない$($n)
コマンド を実行しようとします。1
eval echo \${$n}
に渡されたパラメータを実行しますeval
。展開後、パラメータはecho
および になります${1}
。したがって、eval echo \${$n}
コマンド を実行しますecho ${1}
。
ほとんどの場合、変数置換とコマンド置換 (つまり、 : がある場合$
)は二重引用符で囲む必要があることに注意してください"$foo", "$(foo)"
。変数とコマンドの置換は常に二重引用符で囲みますただし、二重引用符を省略する必要があることがわかっている場合は除きます。二重引用符がない場合、シェルはフィールド分割を実行し (つまり、変数の値またはコマンドからの出力を個別の単語に分割し)、各単語をワイルドカード パターンとして扱います。例:
$ ls
file1 file2 otherfile
$ set -- 'f* *'
$ echo "$1"
f* *
$ echo $1
file1 file2 file1 file2 otherfile
$ n=1
$ eval echo \${$n}
file1 file2 file1 file2 otherfile
$eval echo \"\${$n}\"
f* *
$ echo "${!n}"
f* *
eval
はあまり使用されません。一部のシェルでは、実行時まで名前がわからない変数の値を取得するのが最も一般的な使用方法です。bash では、構文のおかげでこれは必要ありません${!VAR}
。eval
演算子、予約語などを含む長いコマンドを作成する必要がある場合は、それでも便利です。