次の点を考慮してください。
#define _GNU_SOURCE
#define _POSIX_C_SOURCE 200809L
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main(int argc, char *argv[])
{
char *input_arg[2];
char *input_str = NULL;
size_t input_len = 0;
char **args;
ssize_t len;
size_t n;
pid_t child, p;
int status;
if (argc < 2) {
/* No command line parameters. Read command from stdin. */
len = getline(&input_str, &input_len, stdin);
/* Find length excluding the newline at end. */
if (len > (ssize_t)0)
n = strcspn(input_str, "\r\n");
else
n = 0;
if (n > (size_t)0) {
/* Terminate input command before the newline. */
input_str[n] = '\0';
} else {
fprintf(stderr, "No input, no command.\n");
return 1;
}
input_arg[0] = input_str;
input_arg[1] = NULL;
args = input_arg;
} else {
/* Use command line parameters */
argv[argc] = NULL;
args = argv + 1;
}
child = fork();
if (child == (pid_t)-1) {
fprintf(stderr, "Cannot fork: %s.\n", strerror(errno));
return 1;
}
if (!child) {
/* This is the child process. */
errno = ENOENT;
execvp(args[0], args);
fprintf(stderr, "%s: %s.\n", args[0], strerror(errno));
exit(127);
}
do {
p = waitpid(child, &status, 0);
} while (p == (pid_t)-1 && errno == EINTR);
if (p == (pid_t)-1) {
fprintf(stderr, "Lost child process: %s.\n", strerror(errno));
return 127;
}
if (p != child) {
fprintf(stderr, "waitpid() library bug occurred.\n");
return 127;
}
if (WIFEXITED(status)) {
if (!WEXITSTATUS(status))
fprintf(stderr, "Command successful.\n");
else
fprintf(stderr, "Command failed with exit status %d.\n", WEXITSTATUS(status));
return WEXITSTATUS(status);
}
if (WIFSIGNALED(status)) {
fprintf(stderr, "Command died by signal %s.\n", strsignal(WTERMSIG(status)));
return 126;
}
fprintf(stderr, "Command died from unknown causes.\n");
return 125;
}
上記は、指定されている場合はコマンド ライン パラメータを使用し、それ以外の場合は標準入力から 1 つ読み取ります。標準入力はトークン化されていないため、コマンド名のみを指定でき、パラメーターは指定できません。input_arg[]
配列を拡大すると
char *input_arg[4];
割り当てを次のように変更します
input_arg[0] = "/bin/sh";
input_arg[1] = "-c";
input_arg[2] = input_str;
input_arg[3] = NULL;
args = input_arg;
入力文字列は、/bin/sh
シェルを使用して処理されますpopen()
。
割り当てを使用len = getdelim(&input_str, &input_len, '\0', stdin);
および削除して、複数行の入力を許可することもできます。input_str[n] = '\0';
コマンドライン引数バッファーに収まるほど短い限り、シェルはそれらをうまく処理する必要があります (最大長は OS によって異なります)。
シェルが入力を個別のコマンドとパラメーターに分割する方法のルールはかなり複雑であり、それらをエミュレートしようとするべきではありません。代わりに、ユーザーがパラメーターを個別に指定する簡単な方法 (コマンド ライン パラメーター ケースなど) を見つけるか、シェルを使用してそれを実行してください。分割を行わない場合は、おそらく入力行の末尾にある改行を削除する必要があります。
注意すべき点は、 forはexecvp(file, args)
、args[0]
アプリケーションが ($0
またはとしてargv[0]
)見る名前でargs[1]
あり、最初のパラメーターであるということです。\0
各パラメータは、通常 C の文字列と同じように NUL ( ) で終了し、args
ポインタ配列はポインタで終了する必要がありNULL
ます。パラメータがない場合は、args[1] == NULL
.