execv vs execvp, why just one of them require the exact file's path?

后端 未结 1 1246
耶瑟儿~
耶瑟儿~ 2021-01-15 07:39

I have two files in the same directory.

directory/  
| a.c  
| b.c

a.c

#include                                  


        
相关标签:
1条回答
  • 2021-01-15 08:30

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

    0 讨论(0)
提交回复
热议问题