S-JIS[2012-11-17]

exec系関数

execは、C言語で別プログラムを実行(execute)する関数。


概要

exec系の関数は、別プログラムを実行する。
「exec」という関数そのものは無くて、execl・execv・execvp等のいくつかのバリエーションがある。

exec系関数によって別プログラムを実行すると、制御はそのプログラムへ移行し、自プログラムへは戻ってこない。(execの実行に失敗した場合だけ戻ってくる)
自分の処理を続行したい場合はfork()と組み合わせて使用する。
forkと組み合わせる例

別プロセスを実行する方法は、execの他にもsystem(シェルの機能を使ってコマンドを実行する)やpopen(パイプで通信可)等がある。


execの種類

execl(exec「L」)は引数が可変長引数になっていて、execv(exec「v」)は引数が配列になっている。

execlpやexecvp(「p」が付いた関数)は、実行するプログラムにファイル名のみを指定した場合は環境変数PATHで指定された場所から該当プログラムを探す。
(普通は、実行するプログラムを絶対パスで指定する)

execleやexecvpe(「e」が付いた関数)は、環境変数を渡したい場合に使用する。


「/bin/echo」を実行してみる例。

#include <unistd.h>
int main()
{
	execl("/bin/echo", "/bin/echo", "abc", "def", NULL);

	//制御が戻ってくるのは、エラーが発生した場合のみ
	perror("/bin/echo");
	return -1;
}
int main()
{
	char *const args[] = {
		"/bin/echo",
		"abc",
		"def",
		NULL, //C99では末尾にカンマがあってもOK!
	};

	execvp(args[0], args);

	//制御が戻ってくるのは、エラーが発生した場合のみ
	perror(args[0]);
	return -1;
}

第1引数に実行するプログラムのパス(あるいはファイル名)を指定する。
第2引数以降はそのプログラムへの実行時引数(main()のargvと同様の形式)を記述する。
execlとexecvでは書き方は違うが、内容は同じ。すなわち、先頭がコマンド、それ以降が引数で、最後には必ずNULLを入れる

これは、main(int argc, char *argv[])が受け取るargvと全く同じ。
C言語で実行時引数全てを処理するときはargcで判定してループするのが一般的だが、実はargvの最後にはNULLが入っているので、argvだけでループできる。

argcを使用した例 argvのみの例
int main(int argc, char *argv[])
{
	int i;
	for (i = 0; i < argc; i++) {
		printf("%s\n", argv[i]);
	}
}
int main(int argc, char *argv[])
{
	char **p;
	for(p = argv; *p != NULL; p++) {
		printf("%s\n", *p);
	}
}

プログラムの戻り値は、execによって実行したプログラムの戻り値になる。

実行例:

$ gcc exec1.c -o exec1.out

$ ./exec1.out
abc def

実行対象のファイル(プログラム)が見つからない場合はエラーになる。

$ ./exec1.out
/bin/echo1: No shch file or directory

※これは、perror()によって表示されるメッセージ。コロン「:」の左側がperror()に渡した引数、右側がエラー内容。


forkと組み合わせる例

exec系関数は制御が戻ってこない為、自分の処理を続行させたい場合はfork()によって子プロセスを生成し、そこでexecを実行するのが常套手段。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
	pid_t pid = fork();
	if (pid < 0) {
		perror("fork");
		exit(-1);
	} else if (pid == 0) {
		// 子プロセスで別プログラムを実行
		execlp("echo", "echo", "abc", "def", NULL);
		perror("echo");
		exit(-1);
	}

	// 親プロセス
	int status;
	pid_t r = waitpid(pid, &status, 0); //子プロセスの終了待ち
	if (r < 0) {
		perror("waitpid");
		exit(-1);
	}
	if (WIFEXITED(status)) {
		// 子プロセスが正常終了の場合
		printf("child exit-code=%d\n", WEXITSTATUS(status));
	} else {
		printf("child status=%04x\n", status);
	}
	return 0;
}

C言語目次へ戻る / 技術メモへ戻る
メールの送信先:ひしだま