eval は、与えた文字列をもう一度シェルのコマンドとして解釈して実行します。展開(変数・コマンド置換・パス名展開など)が追加で行われ、結果のコマンドが現在のシェルで走ります。
実務では、getopt の整形結果で set -- を行うときや、生成したコマンド列を最終的に実行したいときに使われます。
eval とは?1行で理解する
eval を一言でいうと、「文字列をコマンドとしてもう一度実行するビルトインコマンド」です。
通常、シェルは変数展開やコマンド置換を1回しか行いませんが、eval を使うとその結果に対してもう一段階展開・実行を行います。
そのため「変数の中に変数名が入っている(間接参照)」「動的に組み立てたコマンド文字列を最後に実行したい」といった場面で使われます。
| やりたいこと | コマンド例 |
|---|---|
| 組み立てた文字列をコマンドとして実行 | eval "$cmd" |
getopt の結果を位置パラメータに反映 | eval set -- "$PARSED" |
| 変数名を変数で間接参照 | eval "echo \$$name" |
| 実際に何が実行されるか確認したい | printf '%q\n' "$cmd" |
構文(Syntax)
eval [ARGUMENT ...]
# 引数が複数なら、スペースで連結した1つの文字列として再解釈される
# 戻り値は「再解釈後に実行された最後のコマンド」の終了ステータス
主なオプション一覧
eval自体に一般的なオプションはありません。実用パターンを「項目」として整理します。
| オプション | 説明 | 使用例 |
|---|---|---|
| (なし) | 文字列を再解釈して実行 | eval 'echo hello' |
| 変数で組み立て | 変数の中身をコマンドとして実行 | cmd='printf "%s\n" hi'; eval "$cmd" |
| 連結評価 | 複数引数は連結して1文字列として評価 | eval echo "$USER" is "$SHELL" |
getopt と併用 | 整形した引数列を位置パラメータへ反映 | eval set -- "$(getopt -o ab: -l alpha,bravo: -- "$@")" |
| 間接参照 | 変数名を変数で受けて参照・代入(POSIX流) | name=HOME; eval "echo \$$name" |
実行例
生成したコマンドを実行(基本)
説明:文字列として組み立てたコマンドを実行します。
コマンド
cmd='echo "Project: $PWD"'
eval "$cmd"
出力例(例)
Project: /home/user/work
getopt と組み合わせて安全に引数を整形
説明:getopt の出力を set -- に流し込み、以降の case で処理。
コマンド
OPTS_SHORT='ab:'
OPTS_LONG='alpha,bravo:'
PARSED="$(getopt -o "$OPTS_SHORT" -l "$OPTS_LONG" -n "$0" -- "$@")" || exit 2
eval set -- "$PARSED"
while true; do
case "$1" in
-a|--alpha) echo alpha; shift;;
-b|--bravo) echo "b=$2"; shift 2;;
--) shift; break;;
esac
done
出力例(例)
alpha
b=42
変数名を変数で参照(間接参照)
説明:$name が指す変数(ここでは HOME)の値を参照します。
コマンド
name=HOME
eval "echo \$$name"
出力例(例)
/home/user
危険例:入力混入で意図しない実行(やってはいけない)
説明:外部入力をそのまま eval へ渡すとコマンド注入の危険があります。
コマンド
user_input='Alice; echo HACKED'
eval "echo Hello, $user_input"
出力例(例)
Hello, Alice
HACKED
外部入力は 決して そのまま
evalに渡さないでください。必要なら厳格なバリデーション・無害化や代替手法を使います。
エラー例:再解釈先が構文エラー
説明:再解釈した文字列がシェル構文として不正だとエラー終了します。
コマンド
eval 'echo $(('
echo $?
出力例(例)
bash: syntax error near unexpected token `newline'
2
設定ファイル(KEY=VALUE)を読み込みたいとき
説明:.env のような KEY=VALUE 形式のファイルを読み込みたいとき、eval を使いたくなりますが、値に特殊文字が含まれると事故りやすいため 推奨されません。
信頼できる自分のファイルであれば、source(.)コマンドの方が安全です。
# 非推奨:値にコマンドが混ざると危険
while IFS='=' read -r key val; do
eval "$key=\"$val\""
done < config.env
# 推奨:シェル形式の設定ファイルなら source を使う
set -a
source config.env
set +a
外部から受け取った設定や、信頼できないファイルを読み込む場合は、eval や source を使わずに1行ずつパースして代入する方が安全です。
よくある質問(FAQ)
文字列をもう一度シェルコマンドとして解釈・実行するために使います。getopt の結果を位置パラメータに反映したり、変数名を変数で間接参照したりする場面で使われます。
はい。外部入力や未検証の文字列をそのまま eval に渡すと、二重展開によってコマンド注入が起きやすくなります。可能な限り ${!name}(間接展開)や declare -n、printf -v などの代替手段を使ってください。
bash では ${!name}(間接展開)や declare -n ref=varname(名前参照)を使うと、eval なしで安全に間接参照できます。
再解釈後に実行された最後のコマンドの終了コードがそのまま eval の戻り値になります。if eval ...; then ... fi のように条件分岐に使えます。
関連コマンド
bash -c 'STRING':新しいシェルで文字列をコマンドとして実行(呼び出し元の環境を汚さない)。getopt:引数列の整形に使い、eval set -- "$( ... )"で反映。${!name}(bash拡張):間接展開。evalを使わず変数名から値を参照できる。declare -n ref(bash拡張):名前参照で間接代入/参照を安全に実現。printf -v var ...(bash拡張):文字列の安全な代入。eval "var=..."の代替。xargs:標準入力から受け取ったトークンを安全に引数化してコマンド実行(evalの代替になり得る)。
備考
- セキュリティ:
evalは二重展開を起こすため、外部入力や未検証の文字列を渡すとコマンド注入が極めて起きやすいです。基本方針は「使わない」。代替(配列、間接展開、printf -v、bash -c、xargs)を優先してください。 - 可搬性:
evalは POSIX ユーティリティで、sh互換シェルに広く存在します。 - クォート:
eval "$cmd"のように一重の引数にまとめて渡すのが原則。展開させたい内容は**$cmd内側のクォート**で制御します。 - 戻り値:
eval自身の終了ステータスは、再解釈後に実行された最後のコマンドの終了コードです。if eval ...; then ... fiのように条件判定に使えます。 - デバッグ:実際に何が実行されるか不安なときは、まず
printf '%q\n'で可視化したり、set -x(xtrace)を一時的に有効化して確認します。
参考
- POSIX
eval仕様(The Open Group)
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/eval.html - Bash Reference Manual(Bourne Shell Builtins /
eval)
https://www.gnu.org/software/bash/manual/html_node/Bourne-Shell-Builtins.html#index-eval bash(1)マンページ(組み込みコマンド)
https://man7.org/linux/man-pages/man1/bash.1.html

コメント