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 the a
program call exexXX
to execute the b
program.
I don't understand why if I use execv
I can write just ./a b
in the command line, instead if I use execvp
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
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 do execv("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).