在我的程序中,我正在执行给定的命令并获取结果(日志和退出状态)。另外,我的程序必须支持 shell 特定命令(即包含 shell 特定字符 ~(tild)、|(pipe)、* 的命令)。但是,当我尝试通过我的程序在我的主目录中运行
sh -c ls | wc
时,它失败了,其退出状态为 32512,也在 stderr 流中打印了 "sh: ls | wc: command not found"
。
但有趣的是,如果我在 shell 中运行命令
sh -c ls | wc
,它就能正确工作。
问题是什么?或者更可取的是如何通过我的程序运行 shell 特定命令(即我应该运行哪个命令和哪个参数)?
下面的代码部分位于 fork() 之后的子部分中。它执行命令。
tokenized_command
是std::vector<std::string>
,在我的情况下,"sh", "-c", "ls", "|", "wc"
存储在其中,我也尝试存储在那里"sh", "-c", "\"ls | wc\""
,但结果是相同的。 command
是 char *
存储完整命令行的位置。
boost::shared_array<const char *> bargv(new const char *[tokenized_command.size() + 1]);
const char **argv = bargv.get();
for(int i = 0; i < tokenized_command.size(); ++i)
{
argv[i] = tokenized_command[i].c_str();
printf("argv[%d]: %s\n", i, argv[i]); //trace
}
argv[tokenized_command.size()] = NULL;
if(execvp(argv[0], (char * const *)argv) == -1)
{
fprintf(stderr, "Failed to execute command %s: %s", command, strerror(errno));
_exit(EXIT_FAILURE);
}
附注
我知道使用
system(command)
代替 execvp
可以解决我的问题。但是 system()
会等到命令完成,这对于我的程序来说还不够好。而且我确信在实现 system()
时使用了 exec-family 函数之一,因此问题也可以通过 exec
解决,但我不知道如何解决。
execvp
获取可执行文件的路径以及用于启动该可执行文件的参数。它不需要 bourne shell 命令。
ls | wc
是一个 bourne shell 命令(以及其他命令),由于使用了管道,它无法分解为可执行文件的路径和一些参数。这意味着它不能使用 execvp
来执行。
要使用
execvp
执行 bourne shell 命令,必须执行 sh
并传递 -c
和命令参数。
char *const argv[] = {
"sh",
"-c", "ls | wc", // Command to execute.
NULL
};
execvp(argv[0], argv)
你显然尝试过
char *const argv[] = {
"sh",
"-c", "ls", // Command to execute.
"|", // Stored in called sh's $0.
"wc", // Stored in called sh's $1.
NULL
};
这与 bourne shell 命令相同
sh -c ls '|' wc
。
两者都与 shell 命令非常不同
sh -c ls | wc
。那就这样了
char *const argv[] = {
"sh",
"-c", "sh -c ls | wc", // Command to execute.
NULL
};
您似乎认为
|
和wc
被传递给了sh
,但事实并非如此。 |
是一个特殊字符,它会产生管道,而不是参数。
至于退出代码,
Bits 15-8 = Exit code.
Bit 7 = 1 if a core dump was produced.
Bits 6-0 = Signal number that killed the process.
32512 = 0x7F00
所以它没有因信号而死亡,没有生成核心转储,并且以代码 127 (0x7F) 退出。
127 的含义尚不清楚,这就是为什么它应该伴随着错误消息。您尝试执行程序
ls | wc
,但没有这样的程序。
您应该执行
sh -c 'ls | wc'
。
选项
-c
需要字符串形式的命令。当然,在 shell 中它是有效的,因为生成 ls
和将输出重定向到 wc
以及在单独的 shell 中启动 ls | wc
之间没有区别。