UNIX/Linuxの部屋 コマンド:exec

TOP UNIX/Linuxの部屋 UNIX/Linuxコマンド一覧 用語集 新版 由来/読み方辞書 環境変数マニュアル Cシェル変数 システム設定ファイル システムコール・ライブラリ ネットワークプログラミングの基礎知識 クラウドサービス徹底比較・徹底解説




コマンド exec 現在実行中のシェルに代わり、指定したコマンドを実行する。実行中のシェルのリダイレクトを設定する このエントリーをはてなブックマークに追加

最終更新


UNIX/Linux の exec コマンドは、現在実行中のシェルに代わり、指定したコマンドを実行する sh・bash・csh・tcsh などの内部コマンド (組み込みコマンド・ビルトインコマンド) である。シェルスクリプトから異なるコマンドを実行する際に、プロセス数を抑えてシステムリソースを節約する目的で使用される。また、sh・bash においては実行中のシェルのリダイレクトを変更する。



exec コマンドの概要
まずは基礎知識から。sh・bash・csh・tcsh などのシェルから
% ls
などとコマンドを実行すると、シェルは以下のことを行う。
  • システムコール fork(2) を呼び、子プロセスを生成する。
  • 子プロセスは ls を exec(2) する。
  • 親プロセスであるシェルは、ls の実行が完了するのを待つ (wait する)。

一方、
% exec ls
と exec を使うと、シェルは fork(2) せず、いきなり ls コマンドを exec(2) する (シェルの内部コマンド exec(1) を実行すると、内部でシステムコール exec(2) が呼ばれるということ)。シェルのプロセス情報は ls のプロセスの情報で上書きされる。なお、子プロセスは生成されないので、シェルと ls のプロセス ID は同じになる。

実際にやってみるとわかるが、ssh でログインした状態で、
% exec ls
とすると一瞬 ls の結果が実行されるが、シェルは終了してしまう。これは sshd はシェルの終了を待っているが、シェルのプロセスは ls のプロセスに上書きされ、なおかつ ls のプロセスはすぐに終了してしまうので、ls の終了後、即座に sshd も終了してしまうわけである。

同様に、X Window System 上で kterm などのターミナルを実行中に
% exec ls
とすると、kterm は閉じてしまう。これも、kterm はシェルの終了をを待っているが、シェルのプロセスは ls のプロセスに上書きされるので、ls が終了すると kterm も終了してしまうということだ。

exec コマンドのつかいどころは以下のとおり。

例 1. ~/.xinitrc や ~/.xsession
~/.xinitrc や ~/.xsession では、通常 kterm などの端末エミュレータを数個呼び、twm や fvwm などのウィンドウマネージャを起動する。ここで
#!/bin/sh
kterm -g 102x60+10+10 &
kterm -g 90x52-10-10 &
twm
ではなく、
#!/bin/sh
kterm -g 102x60+10+10 &
kterm -g 90x52-10-10 &
exec twm # ここで exec を使う
と書くとよい。前者は twm を実行している間 (≒ユーザがログインしている間)、sh がずっと twm の終了を待っている。しかし後者は sh のプロセスは twm のプロセスで上書きされてしまうので、前者よりプロセス数が一つ少なくてすむ。

例 2. wrapper スクリプト
例えば FreeBSD 4.7-RELEASE の /usr/bin/pagesize。中身は以下のような sh スクリプトで、sysctl を実行するだけの wrapper (ラッパー) スクリプトである。
#!/bin/sh -
PATH=/bin:/usr/bin:/sbin:/usr/sbin; export PATH
exec sysctl -n hw.pagesize
これも sysctl を実行する間、/bin/sh を待たせなくてよいという効果がある。

同様に、FreeBSD 4.7-RELEASE で調べてみると、
  • /usr/bin/clear は tput を exec する
  • /usr/bin/sockstat は netstat を exec する
  • /usr/bin/perldoc は perl を exec する
  • gs 付属のプログラム (pf2afm、ps2pdf など) は gs を exec する
などなど、多くの wrapper スクリプトが exec でコマンドを呼んでいる。

例3. kubernets のコンテナ内で実行するコマンド
kubernets のコンテナ内で実行するコマンド記述について、sh で複数コマンドを実行する場合、最後に実行するコマンドを exec で呼ぶことで、sh の分のプロセスを節約できる、かもしれない (試していない)。
command: ["/bin/sh"]
args: ["-c", "command1 && command2 && command3 && exec command4"]

exec コマンドのまとめ
exec コマンドによる効果は以下の通り。
  • fork(2) で子プロセスを生成しなくてすむ。fork(2) はかなり重いシステムコールである。fork(2) が重いので、vfork(2) が作られたくらい重い。ただ、現代の UNIX では copy-on-write の効果により、以前ほど fork(2) が重いわけではない。
  • 親プロセスが子プロセスの終了を待つ必要がない。1 プロセス分浮くわけである。

exec コマンドを 1回使うことによる速度向上・メモリ量削減効果はたいしたことはない。ただし数百回・数千回分続くようなら、塵も積もれば…となる。例えば大学のような「1つの大型サーバと数十台の X 端末」という状況で、上記の例 1 を考えると、
twm
の部分を
exec twm
とするだけで、一気に数十プロセスが浮くわけである。

exec コマンドでリダイレクトを変更 【2019-01-27追加】
sh・bash の exec コマンドには、全く異なる用途もある (csh・tcsh の exec ではこの使い方はできない)。

下記のように複数コマンドで同じリダイレクトをしたい場合、
command1 1>>log.txt 2>/dev/null
command2 1>>log.txt 2>/dev/null
command3 1>>log.txt 2>/dev/null

exec コマンドを使うと、シェル内でリダイレクトを設定したままとすることができる。
exec 1>>log.txt 2>/dev/null
command1
command2
command3

元に戻す必要がある場合、n>&m とすることで、ディスクリプタ n を m に退避し、後から元に戻すこともできる。
exec 3>&1 4>&2
exec 1>stdout.log 2>stderr.log

ls exist-file no-exist-file

exec 1>&3 2>&4
ls exist-file no-exist-file

標準入力に対しても exec は可能である。
exec < /etc/hosts
cat -n
とすると下記と同じことが実現できる。
cat -n < /etc/hosts

以下は、標準入力を切り替え、元に戻す例である。
# 標準入力から 1行取得
read v
echo $v

# 標準入力を 3 に退避し、標準入力を /etc/hosts に切り替える。
exec 3>&0
exec < /etc/hosts
cat -n

# 標準入力を元に戻す
exec 0>&3

# 再度標準入力から 1行取得
read v
echo $v
下記のように実行すると、標準入力を一時的に切り替えできていることがわかる。
% (echo aaa ; echo bbb) | sh [上記スクリプト]

関数 exec コマンドを実行 このエントリーをはてなブックマークに追加

exec 系のライブラリ関数には
execl
execle
execlp
exect
execv
execvp
execvP
がある。これらは最終的にシステムコール execve(2) を呼び出す。