190

bash の man ページを読んだ後、この投稿に関して。

evalコマンドが正確に何をするのか、そしてその典型的な用途は何かを理解するのにまだ苦労しています。たとえば、次のようにします。

bash$ set -- one two three  # sets $1 $2 $3
bash$ echo $1
one
bash$ n=1
bash$ echo ${$n}       ## First attempt to echo $1 using brackets fails
bash: ${$n}: bad substitution
bash$ echo $($n)       ## Second attempt to echo $1 using parentheses fails
bash: 1: command not found
bash$ eval echo \${$n} ## Third attempt to echo $1 using 'eval' succeeds
one

ここで正確に何が起こっているのでしょうか?ドル記号とバックスラッシュはどのように問題に結びついているのでしょうか?

4

10 に答える 10

223

eval文字列を引数として取り、コマンド ラインでその文字列を入力したかのように評価します。(複数の引数を渡す場合、それらは最初にスペースで結合されます。)

${$n}は bash の構文エラーです。中括弧内では、変数名のみを使用でき、いくつかの接頭辞と接尾辞を使用できますが、任意の bash 構文を使用することはできず、特に変数展開を使用することはできません。ただし、「この変数に名前が含まれる変数の値」という言い方があります。

echo ${!n}
one

$(…)括弧内に指定されたコマンドをサブシェル (つまり、現在のシェルから変数値などのすべての設定を継承する別のプロセス) で実行し、その出力を収集します。シェルコマンドとしてecho $($n)実行され、その出力が表示されます。は に評価されるため$n、存在しないコマンド の実行を試みます。$n1$($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はあまり使用されません。一部のシェルでは、最も一般的な用途は、実行時まで名前がわからない変数の値を取得することです。${!VAR}bash では、構文のおかげでこれは必要ありません。eval演算子、予約語などを含む長いコマンドを作成する必要がある場合は、引き続き便利です。

于 2012-06-16T16:32:37.527 に答える
45

eval は単に「実行前に式をもう 1 回評価する」と考えてください。

eval echo \${$n}echo $1最初の評価の後になります。注意すべき 3 つの変更点:

  • (バックスラッシュ\$$必要です。それ以外の場合は、 を評価しようとします。これは、許可されていない${$n}という名前の変数を意味します){$n}
  • $nと評価されました1
  • eval消えた

2ラウンド目は基本的にecho $1どちらが直接実行できるかです。

したがって、eval <some command>最初に評価し<some command>(ここでの評価とは、変数を置換したり、エスケープ文字を正しい文字に置き換えたりすることを意味します)、結果の式をもう一度実行します。

eval変数を動的に作成する場合、またはこのように読み取るように特別に設計されたプログラムからの出力を読み取る場合に使用されます。例については、 http://mywiki.wooledge.org/BashFAQ/048を参照してください。リンクには、 の一般的なeval使用方法と、それに伴うリスクも含まれています。

于 2012-06-16T16:19:06.237 に答える
32

私の経験では、eval の「典型的な」使用法は、シェル コマンドを生成して環境変数を設定するコマンドを実行することです。

環境変数のコレクションを使用するシステムがあり、設定する変数とその値を決定するスクリプトまたはプログラムがあるとします。スクリプトまたはプログラムを実行するときはいつでも、フォークされたプロセスで実行されるため、環境変数に対して直接行うことは、終了時に失われます。ただし、そのスクリプトまたはプログラムはエクスポート コマンドを stdout に送信できます。

eval がなければ、stdout を一時ファイルにリダイレクトし、一時ファイルを入手してから削除する必要があります。eval を使用すると、次のことができます。

eval "$(script-or-program)"

引用符が重要であることに注意してください。この(不自然な)例を見てみましょう:

# activate.sh
echo 'I got activated!'

# test.py
print("export foo=bar/baz/womp")
print(". activate.sh")

$ eval $(python test.py)
bash: export: `.': not a valid identifier
bash: export: `activate.sh': not a valid identifier
$ eval "$(python test.py)"
I got activated!
于 2014-10-22T17:29:13.357 に答える
12

eval ステートメントは、eval の引数をコマンドとして受け取り、それらをコマンドラインから実行するようにシェルに指示します。以下のような状況で役立ちます。

スクリプトでコマンドを変数に定義していて、後でそのコマンドを使用する場合は、eval を使用する必要があります。

/home/user1 > a="ls | more"
/home/user1 > $a
bash: command not found: ls | more
/home/user1 > # Above command didn't work as ls tried to list file with name pipe (|) and more. But these files are not there
/home/user1 > eval $a
file.txt
mailids
remote_cmd.sh
sample.txt
tmp
/home/user1 >
于 2014-09-23T09:12:59.513 に答える
0

「実行前にもう一度式を評価する」という回答が好きで、別の例で明確にしたいと思います。

var="\"par1 par2\""
echo $var # prints nicely "par1 par2"

function cntpars() {
  echo "  > Count: $#"
  echo "  > Pars : $*"
  echo "  > par1 : $1"
  echo "  > par2 : $2"

  if [[ $# = 1 && $1 = "par1 par2" ]]; then
    echo "  > PASS"
  else
    echo "  > FAIL"
    return 1
  fi
}

# Option 1: Will Pass
echo "eval \"cntpars \$var\""
eval "cntpars $var"

# Option 2: Will Fail, with curious results
echo "cntpars \$var"
cntpars $var

オプション 2 の興味深い結果は、次のように 2 つのパラメーターを渡したということです。

  • 最初のパラメータ:"value
  • 2 番目のパラメーター:content"

直観に反する人にとってはどうですか?追加evalはそれを修正します。

https://stackoverflow.com/a/40646371/744133から適応

于 2016-11-17T04:38:03.187 に答える