execve:如何用多个命令而不是单个命令初始化char *argv[ ]
?
如果我想执行4个命令,我可以使用下面的语句吗?
char *argv[4][ ] = { {...}, {...}, {...} };
而使用execve来执行它们,我可以使用一个从1到4的variables的循环吗?
只有一个execve
调用你不能执行多个命令。 在一个循环中,你需要fork
你的程序来执行多个execve
调用。 在execve的手册中写道:
execve()不会成功返回,调用进程的文本,数据,bss和堆栈将被加载的程序覆盖。 […]
返回值
成功时,execve()不返回,错误返回-1,并适当地设置errno。
使用fork的方法:
输出:
Hello 1 Hello 2 Hello 3
码:
#include <unistd.h> #include <stdio.h> int main(void) { int idx; char *argv[][4] = { {"/bin/sh", "-c", "echo Hello 1", 0}, {"/bin/sh", "-c", "echo Hello 2", 0}, {"/bin/sh", "-c", "echo Hello 3", 0} }; for (idx = 0; idx < 3; idx++) { if (0 == fork()) continue; execve(argv[idx][0], &argv[idx][0], NULL); fprintf(stderr, "Oops!\n"); } return 0; }
使用命令串联的方法:
解决方法是使用shell来连接命令:
输出:
Hello 1 Hello 2
码:
#include <unistd.h> #include <stdio.h> int main(void) { char *argv[] = {"/bin/sh", "-c", "echo Hello 1 && echo Hello 2", 0}; execve(argv[0], &argv[0], NULL); fprintf(stderr, "Oops!\n"); return 0; }
exec*()
系列函数用新的可执行文件替换当前正在运行的进程。 所以没有办法执行多个命令,因为一旦你调用execve()
,你自己的程序就不会再运行 – 这个进程正在执行新的程序。
现有的答案显示了使用fork()
创建新进程并在那里调用exec*()
函数的“经典”方法。 这对于复制一些由exec*()
调用立即替换的与进程有关的资源来说有一点开销。 为了解决这个低效率, vfork()
被发明出来了。 vfork()
不应该做任何的复制,因此除了在由vfork()
创建的孩子中调用_exit()
或exec*()
函数之外的任何其他操作都是未定义的行为 。
这是一个巨大的错误来源,后来又从POSIX标准中删除了,所以你不应该在现代程序中使用vfork()
。 现在,有一个新的方法来解决这个问题: posix_spawn()
。 这个函数直接用新的可执行文件创建一个新的进程。 由于这是你想要在你的问题中实现的很好的匹配,下面是一个小例子:
#include <stdio.h> #include <spawn.h> #include <sys/types.h> #include <sys/wait.h> extern char **environ; int main(void) { char *argv[][3] = { { "echo", "First command", 0}, { "echo", "Second command", 0} }; for (int i = 0; i < 2; ++i) { pid_t pid; if (posix_spawn(&pid, "/bin/echo", 0, 0, argv[i], environ) != 0) { fputs("Error spawning child.\n", stderr); } else { // could get exit code etc here, see // https://linux.die.net/man/2/waitpid wait(0); } } return 0; }
那么,你不能。 只是。 execve(2)
系统调用不是执行几个命令的调用,而只是通过将新程序映像加载到其虚拟空间中来覆盖进程分配的空间。 这意味着从execve(2)
执行几个命令的唯一方法是加载一个shell并使其执行几个命令。 你必须明白,这意味着加载一个程序到内存中,并开始执行它,而不是执行一般的程序或shell脚本。 当你明白这一点的时候,你将能够理解加载一个shell来连续执行几个命令的原因(如下面的例子或者system(3)
函数所调用的那样)
execlp("/bin/bash", "bash", "-c", "echo foo; echo bar", NULL);
例如,将执行两个不同的echo命令,这些命令通过单个参数字符串传递给bash(1)
shell。
为简单起见,我使用了类似的execvp(2)
系统调用,但execve(2)
(这是完整的通用系统调用接口,尽管两者都转换为相同的exec()
系统调用也是如此。