言語
日本語
English

Caution

お使いのブラウザはJavaScriptが無効になっております。
当サイトでは検索などの処理にJavaScriptを使用しています。
より快適にご利用頂くため、JavaScriptを有効にしたうえで当サイトを閲覧することをお勧めいたします。

Linux & Mac & Bashコマンド辞典

  1. トップページ
  2. Linux & Mac & Bashコマンド辞典
  3. eval / exec

eval / exec

対応: 全Linux
macOS(2001 Cheetah)
Bash 1.0(1989)

eval は文字列をコマンドとして評価・実行する組み込みコマンドです。exec は現在のシェルプロセスを別のコマンドに置き換えるか、ファイルディスクリプタを操作します。どちらも汎用性の高い機能ですが、使い方を誤るとセキュリティ上の問題やスクリプトの予期しない終了を引き起こすため、仕組みをよく理解した上で使うことが重要です。

構文

eval は引数として渡された文字列を連結し、シェルコマンドとして評価・実行します。

eval [文字列 ...]

exec にコマンドを渡すと、現在のシェルプロセスがそのコマンドに置き換わります(新しいプロセスは作成されません)。

exec コマンド [引数 ...]

exec をコマンドなしでリダイレクトだけ指定すると、現在のシェルの標準入出力を切り替えます。

exec >ファイル 2>&1
exec <ファイル

eval と exec の比較

コマンド動作プロセス主な用途
eval文字列をコマンドとして評価・実行現在のシェルで実行(サブシェルなし)動的変数名・コマンド文字列の組み立て
exec コマンド現在のシェルを別コマンドに置き換え新プロセスを作らず上書きスクリプト末尾でコマンドに引き継ぐ
exec リダイレクト現在のシェルの FD を切り替えプロセス変化なしスクリプト全体のログ出力先を変更

サンプルコード

eval を使った動的変数名の参照です。変数名を文字列で組み立てて、その変数の値を取り出します。

time_leap.sh
#!/bin/bash
# 世界線ごとのメッセージを動的に参照する

msg_alpha="α世界線: シュタインズゲートへの道"
msg_beta="β世界線: 別の運命"
msg_steinsgate="Steins;Gate世界線: 目標に到達"

for worldline in alpha beta steinsgate; do
    eval "echo \$msg_${worldline}"
done

実行するコマンドは次の通りです。

bash time_leap.sh
α世界線: シュタインズゲートへの道
β世界線: 別の運命
Steins;Gate世界線: 目標に到達

eval でコマンド文字列を組み立てて実行する例です。条件によって異なるコマンドを動的に構築します。

gadget_launcher.sh
#!/bin/bash
# 未来ガジェットを動的に起動するスクリプト

gadget_no=8
action="send"
target="okabe@example.com"

# コマンド文字列を動的に組み立てる
cmd="mail -s 'Future Gadget No.${gadget_no} activated' ${target}"
echo "実行するコマンド: $cmd"
eval "$cmd <<< 'D-Mail: operation complete'"

exec でスクリプト全体の出力をログファイルにリダイレクトする例です。exec 以降の全コマンドの標準出力と標準エラー出力がファイルに書き込まれます。

reading_steiner.sh
#!/bin/bash
# スクリプト全体の出力をログファイルに記録する

LOG_FILE="reading_steiner.log"

# 以降の stdout と stderr をすべてログファイルへ
exec >"$LOG_FILE" 2>&1

echo "オペレーション・スタート: $(date)"
echo "世界線変動率: 0.571024%"

# 何らかの処理
ping -c 1 example.com

echo "オペレーション終了: $(date)"

実行するコマンドは次の通りです。

bash reading_steiner.sh
cat reading_steiner.log
オペレーション・スタート: Thu Apr  9 10:00:00 JST 2026
世界線変動率: 0.571024%
PING example.com (93.184.216.34): 56 data bytes
64 bytes from 93.184.216.34: icmp_seq=0 ttl=56 time=150.3 ms
オペレーション終了: Thu Apr  9 10:00:01 JST 2026

exec でプロセスを別のコマンドに置き換える例です。exec 以降のスクリプトコードは実行されません。

worldline_shift.sh
#!/bin/bash
# 現在のシェルプロセスを /bin/bash に置き換える

echo "置き換え前: PID=$$"

# 現在のシェルが /bin/bash に置き換わる
# この exec より後のコードは実行されない
exec /bin/bash --login

echo "この行は実行されない"

実行するコマンドは次の通りです。

bash worldline_shift.sh
置き換え前: PID=12345
bash-5.2$

概要

eval はシェルの二重評価(double evaluation)を行います。まず変数展開・コマンド置換などが行われて文字列が確定し、その確定した文字列をもう一度コマンドとして解釈・実行します。この二重評価により、通常の構文では書けない動的なコードを実現できます。

exec コマンド を実行すると、現在のシェルプロセス(PID)がそのコマンドに上書きされます。fork(子プロセス生成)を行わないため、スクリプトの末尾で exec を使うと余分なシェルプロセスを消費せずに別のプログラムに処理を引き継げます。

exec リダイレクト(コマンドなし)はプロセスの置き換えを行わず、現在のシェルのファイルディスクリプタ(FD)だけを切り替えます。スクリプト冒頭に exec >logfile 2>&1 と書くと、以降の全出力をログファイルに向けられます。

セキュリティ上の注意: eval にユーザー入力を渡さない

eval に外部からの入力(ユーザー入力・ファイル内容・環境変数)を直接渡すと、任意のコマンドを実行されるインジェクション脆弱性が生じます。これは Bash スクリプトで最も危険なパターンのひとつです。

gadget_launcher_ng.sh
#!/bin/bash
# 危険な例: ユーザー入力をそのまま eval に渡す

read -p "名前を入力してください: " user_input
eval "echo こんにちは、${user_input}さん"

上記のスクリプトに悪意ある入力を渡すと、任意のコマンドが実行されます。

bash gadget_launcher_ng.sh
名前を入力してください: Okabe; rm -rf /tmp/important_data; echo hacked
こんにちは、Okabe
hacked

上記では rm -rf /tmp/important_dataecho hacked がセミコロンで区切られ、eval によって実行されます。ユーザー入力を扱う場合は eval を避け、配列や安全な文字列処理を使います。

#!/bin/bash
# 安全な例: eval を使わずに配列でコマンドを組み立てる

cmd_args=("echo" "こんにちは、${user_input}さん")
"${cmd_args[@]}"

よくあるミス

よくあるミス1: eval のインジェクション脆弱性

eval の危険性は意図しないところから入力が来る場合にも生じます。スクリプトの引数($1)や環境変数を eval に渡すパターンは特に要注意です。

check_var_ng.sh
#!/bin/bash
# 危険: スクリプト引数をそのまま eval に渡す

target_var=$1
eval "echo \$$target_var"

修正後は次の通りです。

bash check_var_ng.sh 'PATH; cat /etc/passwd'
/usr/local/bin:/usr/bin:/bin
root:x:0:0:root:/root:/bin/bash
...

引数に ; を含む文字列を渡すと、変数参照と無関係なコマンドが実行されます。変数名として使う場合は、英数字とアンダースコアだけを受け付けるバリデーションが必要です。

#!/bin/bash
# 安全: 変数名のバリデーションを行ってから参照する

target_var=$1

# 変数名として有効な文字(英数字・アンダースコア)のみ許可
if [[ ! "$target_var" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]]; then
    echo "エラー: 無効な変数名です: $target_var" >&2
    exit 1
fi

eval "echo \$$target_var"

よくあるミス2: exec 後は元のスクリプトに戻れない

exec でプロセスを置き換えると、現在のシェルプロセスは消滅します。exec より後のコードは一切実行されず、元のスクリプトに戻ることもできません。

worldline_shift.sh
#!/bin/bash
# exec 後のコードは実行されない

echo "処理を開始します"
echo "ログを記録します"

exec /usr/bin/python3 -c "print('Pythonに切り替わりました')"

# 以下は実行されない
echo "この行は実行されません"
cleanup_function

修正後は次の通りです。

bash worldline_shift.sh
処理を開始します
ログを記録します
Pythonに切り替わりました

「exec の後に後片付けをしたい」という意図でコードを書くと、後片付けが実行されずリソースリークが起きます。クリーンアップ処理は exec より前に完了させるか、trap コマンドと組み合わせる方法があります(trap はプロセス置き換え後には機能しない点に注意)。

ファイルディスクリプタのリダイレクトだけを行う exec リダイレクト(コマンドなし)はプロセスを置き換えないため、スクリプトは継続します。

fd_redirect.sh
#!/bin/bash
# exec リダイレクトはプロセスを置き換えない

exec >"reading_steiner.log" 2>&1   # FD の切り替えのみ

echo "この行は実行される(ログファイルに書かれる)"
echo "スクリプトは継続します"

記事の間違いや著作権の侵害等ございましたらお手数ですがまでご連絡頂ければ幸いです。