1. Overview
In this article, we’ll talk about the exec() family of functions, what they do, and the differences between them.
These functions are used to execute a file, and they replace the current process image with a new process image once they are called. Even though they are very similar, there are differences between them, and each one of them receives different information as arguments.
2. execl()
execl() receives the location of the executable file as its first argument. The next arguments will be available to the file when it’s executed. The last argument has to be NULL:
int execl(const char *pathname, const char *arg, ..., NULL)
Let’s look at an example. We need to make sure to include unistd.h:
#include <unistd.h>
int main(void) {
char *file = "/usr/bin/echo";
char *arg1 = "Hello world!";
execl(file, file, arg1, NULL);
return 0;
}
The command we are running is echo which is located at /usr/bin/echo. By convention, the first argument available to a program needs to be the program itself.
Now we can compile and run the code:
$ gcc execl.c -o execl
$ ./execl
Hello world!
echo has successfully printed the output on the screen.
3. execlp()
execlp() is very similar to execl(). However, execlp() uses the PATH environment variable to look for the file. Therefore, the path to the executable file is not needed:
int execlp(const char *file, const char *arg, ..., NULL)
Let’s see an example:
#include <unistd.h>
int main(void) {
char *file = "echo";
char *arg1 = "Hello world!";
execlp(file, file, arg1, NULL);
return 0;
}
Now we can compile and run it:
$ gcc execlp.c -o execlp
$ ./execlp
Hello world!
Since echo is already located in the PATH environment variable, we didn’t have to specify its location.
4. execle()
If we use execle(), we can pass environment variables to the function, and it’ll use them:
int execle(const char *pathname, const char *arg, ..., NULL, char *const envp[])
For example:
#include <unistd.h>
int main(void) {
char *file = "/usr/bin/bash";
char *arg1 = "-c";
char *arg2 = "echo $ENV1 $ENV2!";
char *const env[] = {"ENV1=Hello", "ENV2=World", NULL};
execle(file, file, arg1, arg2, NULL, env);
return 0;
}
Now we can compile and run it:
$ gcc execle.c -o execle
$ ./execle
Hello World!
bash successfully received the environment variables and printed them on the screen.
5. execv()
execv(), unlike execl(), receives a vector of arguments that will be available to the executable file. In addition, the last element of the vector has to be NULL:
int execv(const char *pathname, char *const argv[])
Let’s see an example:
#include <unistd.h>
int main(void) {
char *file = "/usr/bin/echo";
char *const args[] = {"/usr/bin/echo", "Hello world!", NULL};
execv(file, args);
return 0;
}
Now we can compile and run it:
$ gcc execv.c -o execv
$ ./execv
Hello world!
6. execvp()
Just like execlp(), execvp() looks for the program in the PATH environment variable:
int execvp(const char *file, char *const argv[])
For example:
#include <unistd.h>
int main(void) {
char *file = "echo";
char *const args[] = {"/usr/bin/echo", "Hello world!", NULL};
execvp(file, args);
return 0;
}
Now we can compile and run it:
$ gcc execvp.c -o execvp
$ ./execvp
Hello world!
We didn’t need to specify the exact location of the program this time.
7. execve()
We can pass environment variables to execve(). In addition, the arguments need to be inside a NULL-terminated vector:
int execve(const char *pathname, char *const argv[], char *const envp[])
For example:
#include <unistd.h>
int main(void) {
char *file = "/usr/bin/bash";
char *const args[] = {"/usr/bin/bash", "-c", "echo Hello $ENV!", NULL};
char *const env[] = {"ENV=World", NULL};
execve(file, args, env);
return 0;
}
Let’s compile and run:
$ gcc execve.c -o execve
$ ./execve
Hello World!
We successfully passed the environment variables and the arguments to bash and printed them on the screen.
8. Conclusion
In this article, we learned what each member of the exec() family of functions does and the differences between them.