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).