execv vs execvp, why just one of them require the exact file's path?
If the program name argument contains no slashes, the execvp()
function looks for the program to execute in the directories listed on your PATH environment variable. If you don't have .
(the current directory) on your PATH and you aren't in one of the directories listed on your path, a plain name like b
will not be executed, even if b
is in the current directory. If the name contains a slash, it can be relative (./b
) or absolute (/home/someone/src/programs/b
) and it will be interpreted as a file name to be executed without consulting the PATH environment variable.
By contrast, execv()
treats a plain b
in the program name argument as ./b
— the name of the file in the current directory and executes it if it is present, and fails if it is located somewhere else.
At one time, there was a comment that asked:
Are you saying if you have an executable b in
.
and you doexecv("b", b_args)
, it will get executed?
On a normal Unix box, yes.
Code b.c
:
#include <stdio.h>
int main(void)
{
puts("Hello");
return 0;
}
Code a.c
:
#include <stdio.h>
#include <unistd.h>
int main(void)
{
char *argv[] = { "b", 0 };
execv(argv[0], argv);
fprintf(stderr, "failed to execute '%s'\n", argv[0]);
return 1;
}
Running these:
$ (PATH=$(clnpath "$PATH" ".:$PWD"); echopath PATH; ./a)
/Users/jleffler/bin
/opt/informix/12.10.FC6/bin
/Users/jleffler/oss/bin
/Users/jleffler/oss/rcs/bin
/usr/local/mysql/bin
/opt/gcc/v7.3.0/bin
/Users/jleffler/perl/v5.24.0/bin
/usr/local/bin
/usr/bin
/bin
/opt/gnu/bin
/usr/sbin
/sbin
Hello
$
The clnpath
script modifies the string provided as its first argument ("$PATH"
) by removing any occurrences of any of the directory names listed in its second path-like argument (".:$PWD"
) — it's how I edit my PATH on the fly when I need to. The echopath
script echoes the directories on PATH
(or any other path-like variable, or it will process the result of expanding a pathlike variable, such as "$PATH"
), one per line — the output shows that neither .
nor /Users/jleffler/soq
(which is where I run the program) is on $PATH
in the sub-shell. The ./a
runs the code from a.c
(it would not be executed without that ./
in front), which in turn runs the code from b.c
, which produces the Hello
. (If there is some system where this does not work, please identify it.)
I could also arrange for b.c
to be:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
puts("Hello");
const char *env = "PATH";
char *val = getenv(env);
if (val == 0)
val = "<nothing>";
printf("%s=%s\n", env, val);
return 0;
}
which would print the value of $PATH
directly from the executable (to verify that neither .
nor the value of the current working directory is listed).
Related videos on Youtube
ikeDiM
Updated on June 04, 2022Comments
-
ikeDiM about 2 years
I have two files in the same directory.
directory/ | a.c | b.c
a.c
#include <stdio.h> #include <string.h> #include <sys/types.h> #include <unistd.h> int main(int argc, char *argv[]) { pid_t pid; int status; int wret; if ((pid = fork()) < 0) printf("error"); else if(pid == 0) { printf("%s", argv[1]); execv(argv[1], &argv[1]); } else { /* respawn */ if ((wret = wait(&status)) != -1) execv(argv[1], &argv[1]); } return 0; }
b.c is just a simple program that print "hello".
I want to run
./a b
from the command line to make thea
program callexexXX
to execute theb
program.I don't understand why if I use
execv
I can write just./a b
in the command line, instead if I useexecvp
I have to write./a ./b
.The
man exec
page is not clear because it reports"The initial argument for these functions is the name of a file that is to be executed."
Thanks
-
chux - Reinstate Monica over 6 yearsHmmm, environment variable
PATH
likely missing fromexecvp()
. It would make more sense to post the exact failing code than the working one. -
user253751 over 6 yearsWell what do you think is the difference between execv and execvp?
-
Jonathan Leffler over 6 yearsAfter
execv()
returns, you should probably be reporting an error (ifexecv()
returns, it failed) and taking steps to exit, In this code, you aren't in a loop, so it doesn't hurt much, but in more complex code, not handling the error explicitly will lead to gross confusion. (Have you ever tried typing when two processes are both trying to read your terminal at the same time? It isn't fun!) Your messages should have newlines at the end to ensure they appear — add afflush(stdout)
if you might pipe the output totee
or anything like that. And errors should be reported tostderr
. -
Jonathan Leffler over 6 yearsAlso, which call is using
execvp()
— I see two calls toexecv()
.
-