C 言語で execlp 関数を使用する
この記事では、C 言語で execlp
関数を使用する方法について複数の方法を紹介します。
C で execlp
を使用してファイル名を使用して新しいプログラムを実行する
関数 exec
ファミリーは、プロセスメモリへの新しいプログラムのロードを管理するために、下位レベルの execve
システムコールに代わる API として提供されます。このファミリには 6つの独立した関数があり、ほとんどの場合、結果というよりもパラメータが異なります。execlp
関数はユーザにファイル名を指定するオプションを与えるもので、プログラムは現在の環境変数 PATH
にリストされているディレクトリ内で検索されます。
ファイル名にスラッシュが含まれている場合は、相対パス名か絶対パス名として扱われます。次の例では、シェルと同様の動作をするプログラムを実装します。すなわち、このプログラムはユーザからプログラム名を受け取り、子プロセスとして実行します。親プロセスは待機し、子プロセスが戻ると、制御はユーザ入力の次の反復処理に進みます。プログラムはユーザがCtrl+Dのようなキーストロークで終了させる必要があります(OS に依存します)。シェルコマンドの中には実行できないものもあること、そして最も重要なことは、コマンドラインの引数が渡されず、代わりに couldn't execute
と表示されることです。
#include <sys/wait.h>
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "unistd.h"
enum { MAXLINE = 256, MAXARGS = 48 };
int main(int argc, char *argv[]) {
char buf[MAXLINE];
pid_t pid;
int status;
char *str1, *token;
printf("%% ");
while (fgets(buf, MAXLINE, stdin) != NULL) {
if (buf[strlen(buf) - 1] == '\n') buf[strlen(buf) - 1] = 0;
if ((pid = fork()) < 0) {
perror("fork");
} else if (pid == 0) {
execlp(buf, buf, (char *)NULL);
printf("couldn't execute: %s", buf);
exit(127);
}
if (waitpid(pid, &status, 0) < 0) perror("waitpid");
printf("%% ");
}
exit(EXIT_SUCCESS);
}
execlp
は可変関数であり、可変数の引数を取ることができます。しかし、最初と最後の引数は固定されており、ファイル名へのポインタを表し、それに対応して char*
に NULL
をキャストします。NULL ポインタをキャストすることは、関数が動作するために必須であり、可変数の引数の終わりを示すことにも注意してください。要するに、2 番目の位置の引数はプログラムのコマンドライン引数を指定しなければならず、そのうちの最初の引数はファイル名そのものでなければならません。
あるいは、前の例を実装して、引数を使ってコマンドを実行できるようにすることもできます。この場合、引数リストを配列として受け取る execvp
関数を利用しました。また、strtok
を用いてユーザ入力を解析し、スペースで区切られた各文字列を引数として渡しました。その結果、よりシェルプログラムに近いエミュレーションが可能となった。
#include <sys/wait.h>
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "unistd.h"
enum { MAXLINE = 256, MAXARGS = 48 };
int main(int argc, char *argv[]) {
char buf[MAXLINE];
char *args[MAXARGS];
pid_t pid;
int status, args_num = 0;
char *str1, *token;
printf("%% ");
while (fgets(buf, MAXLINE, stdin) != NULL) {
if (buf[strlen(buf) - 1] == '\n') buf[strlen(buf) - 1] = 0;
str1 = strdup(buf);
for (int j = 0;; j++, str1 = NULL) {
token = strtok(str1, " ");
if (token == NULL) break;
args[j] = token;
args_num += 1;
printf("%d: %s\n", j + 1, args[j]);
}
free(str1);
args[args_num] = (char *)NULL;
if ((pid = fork()) < 0) {
perror("fork");
} else if (pid == 0) {
execvp(args[0], &args[0]);
printf("couldn't execute: %s", buf);
exit(127);
}
if (waitpid(pid, &status, 0) < 0) perror("waitpid");
printf("%% ");
}
exit(EXIT_SUCCESS);
}