答え1
私は2つの...一般的な...使用事例を知っていますeval
。
パラメータ処理
getopt
:[T]この実装は引用された出力を生成することができ、これはシェルで再解釈されるべきです(通常はevalコマンドを使用して)
設定するSSHエージェント:
[T]エージェントは、呼び出しシェルで評価できる必須シェルコマンド(sh(1)またはcsh(1)構文を生成できます)を印刷します(たとえば、
eval `ssh-agent -s`
sh(1)またはksh(1)などのBourneタイプシェルの場合)eval `ssh-agent -c`
csh(1)と派生。
どちらの用途にも異なるオプションがあるかもしれませんが、どちらにも驚かないでしょう。
答え2
Bash & Co. slicing などの拡張子なしで POSIX シェルから最後の引数を取得するには、次のように${@: -1}
します。
eval "v=\${$#}"
$#
これはシェルの内部であり、スクリプト/関数の引数の数だけを含む可能性があるため、不快なトリックに脆弱ではありません。
これは私が思いついたものではありません。スティーブン・チャジェラス コメント。でも言及この回答evalを使用しない理由と時期は何ですか?。
答え3
より良い選択肢が見つかりませんでしたが、eval
作業をきちんと完了した「実際の」ユースケースのいくつかの例です。
「条件付き拡張」のユースケース。ここでは$rmsg_pfx
、値がある場合にのみリダイレクトを使用したいと思います。
eval 'printf -- %s%s\\n "$rmsg_pfx" "$line" '"${rmsg_pfx:+>&2}"
なしではこれを行うことはできません。eval
これは、ビットがリダイレクトではなく>&2
パラメータに拡張されるためです。printf
$rmsg_pfx
nullであることを確認するために行をコピーすることができますが、それは…まあ…コードの重複になります。
リダイレクトについて言えば、「間接」のユースケースとして、私は{varname}>&...
リダイレクト構文に頼るのが好きです。私は次のようにPOSIXlyをモックします。
# equivalent of bash/ksh `exec {rses_fd0}>&- {rses_fd1}<&-` redirection syntax
eval "exec $rses_fd0>&- $rses_fd1<&-"
上記はfdsを閉じることです。同様に、fdsオープンをシミュレートするために同様の間接的な作業を行っています。明らかにおよび$rses_fd0
は$rses_fd1
スクリプトの内部変数であり、最初から最後までスクリプトによって完全に制御されます。
eval
時々、他のシェルを邪魔することなく、特定のシェルのためのシェルコードの断片を単に「保護」する必要があるかもしれません。
たとえば、次のコードは、一部のシェル固有の最適化も含む移植可能な(POSIXly)スクリプトからのものです。
sochars='][ (){}:,!'"'\\"
# NOTE: wrapped in an eval to protect it from dash which croaks over the regex
eval 'o=; while [[ "$s" =~ ([^$sochars]*)([$sochars])(.*) ]]; do
...
done'
dash
その構文が実際のコードパスにまったく入らない場合でも、語彙レベルでは不明な(しかし直接的な)構文によってブロックされます。
別の意味では、「保護」のもう一つのユースケースです。時々私は保存と復元の目的のために「不思議な」名前を作るにはあまりにも怠惰です。たとえば、次の場合は$r
値だけを保持したいと思います。
# wrapped in eval just to make sure that $r is not overwritten by (the call chain of) coolf
eval '
coolf "$tmp" || return "$lerrno"'"
return $r
"
実際、私は次のようにクリーンアップ操作を実行し、ループのコレクションの終了状態を維持するために上記のトリックを頻繁に使用します。
done <&3
eval "unset ret vals; exec 3<&-; return $?"
}
または「遅延実行」などの上記のような状況で:
done
# return boolean set by loop while also unsetting it
eval "unset ok; ${ok:-false}"
}
上記の2つのコードスニペットの暗黙の意図は、関数がインタラクティブに実行されることを意図した場合、関数の実行に「アーティファクト」を残さないことです。後者の場合は、次のようにすることができます。
[ "${ok:-false}" = false ] && { unset ok; return 1; } || { unset ok; return 0; }
しかし、私は見るには粗いようです。
最後に、小さな動作の変更や呼び出し元の一部のフックをサポートするために、呼び出しに基づいて関数を少し変更または拡張する必要がある場合があります。コールバックに似ていますが、「インライン」方式ではあまり面倒ではありません。特に、フックの断片が関数の独自のパラメータ$@
にアクセスする必要がある場合は、さらにそうです。もちろん、変数によって提供され、後でeval
関数によって編集されるこれらのフラグメント自体は、完全に静的/手動で作成または事前制御/衛生処理されます。
答え4
eval
私が見た実際の用途の1つは次のとおりです。Fluxbox.startfluxbox.dbus.diff.gz
スラックウェアから。次のようになります。
# Start DBUS session bus:
if [ -z "$DBUS_SESSION_BUS_ADDRESS" ]; then
eval $(dbus-launch --sh-syntax --exit-with-session)
fi
この使用法はeval
何百万人もの人々によってテストされていませんが(Slackwareはあまり一般的ではありません)、作業を完了します。それでも私はeval
シェルスクリプトでこれを避けようとします。たとえば、配列を実装したり、変数間接指定を実行したりする必要があると感じた場合は、Bashに切り替えます。