Reputation: 171
execve: how can I initialise char *argv[ ]
with multiple commands instead of a single command?
If I want to execute 4 commands, can I use the following statement?
char *argv[4][ ] = { {...}, {...}, {...} };
And to execute them using execve, can I use a loop with a var from 1 to 4?
Upvotes: 5
Views: 3671
Reputation: 12698
Well, you cannot. Simply. The execve(2)
system call is not a call to execute several commands, but only to overwrite the space allocated by a process by loading a new program image into its virtual space. This means the only way to execute several commands from execve(2)
is to load a shell and make it execute several commands. You have to understand that this means loading a program into memory and begin executing it, and not executing programs or shell scripts in general. When you understand this, you'll be able to understand the reason of loading a shell to execute several commands in a row (like the below example or what the system(3)
function call does)
execlp("/bin/bash", "bash", "-c", "echo foo; echo bar", NULL);
for example, will execute two different echo comands which are passed to a bash(1)
shell in a single parameter string.
I've used the similar execvp(2)
system call for simplicity, but the same is applicable to the execve(2)
(which is the full general system call interface, although both translate to the same exec()
system call.
Upvotes: 0
Reputation:
The exec*()
family of functions replaces the current running process with a new executable. So there's no way to execute multiple commands, because once you call execve()
, your own program doesn't run any more -- the process is now executing the new program.
The existing answer shows the "classic" approach of using fork()
to create a new process and calling an exec*()
function there. This has a little overhead of copying some process-related resources that are immediately replaced again by the exec*()
call. To solve this inefficiency, vfork()
was invented. vfork()
isn't supposed to do any of the copying and therefore, doing anything other than calling _exit()
or one of the exec*()
functions in a child created by vfork()
is undefined behavior.
This is a huge source of bugs and was later removed again from the POSIX standard, so you should not use vfork()
in a modern program. Nowadays, there's a new way to solve this problem: posix_spawn()
. This function creates a new process directly with a new executable. As this is a good match for what you're trying to achieve in your question, here's a tiny usage example:
#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;
}
Upvotes: 1
Reputation: 5631
You can not execute multiple commands with just one execve
call. In a loop you will need to fork
your program to execute multiple execve
calls. In the manpage of execve it's written:
execve() does not return on success, and the text, data, bss, and stack of the calling process are overwritten by that of the program loaded. [...]
Return value
On success, execve() does not return, on error -1 is returned, and errno is set appropriately.
Method using fork:
Output:
Hello 1
Hello 2
Hello 3
Code:
#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;
}
Method using command concatenation:
A workaround would be to concate the commands using the shell:
Output:
Hello 1
Hello 2
Code:
#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;
}
Upvotes: 4