使用execvp使用完整路径名错误执行命令行参数

问题描述

我对Linux很陌生,所以请多多包涵。我正在尝试从终端执行命令行参数,每个参数都是可执行文件的完整路径。例如,这样的命令行:

./cmdarguments /Desktop/darren/lab01/c_ex1/a.out

表示cmdarguments是我的父程序,下面的命令行是我希望执行的文件的路径,该文件只打印出hello world

到目前为止,这是我的代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc,char* argv[])
{
    int counter;
    
    for(counter = 0; counter < argc; counter++){
        
        pid_t pid = fork();
        if(pid < 0)
        {
            perror("Forking Failed\n");
            exit(1);
        }
        else if(pid == 0)
        {
            char *args[] = {argv[counter],NULL};
            execvp(args[0],args);
            printf("Command completed.\n");
            exit(0);
        }

        
    }
    exit(0);
    
}

我了解我的代码中可能遗漏了很多东西,而从在线阅读的内容来看,我只会更加困惑自己。仅使用forkexec意味着这是一个简单的任务。现在,我当前的输出仅为Command completed,无论我用作第二个命令行参数的内容似乎没有变化,它始终为Command completed

解决方法

关于程序的一些注意事项:

  1. exec*函数家族在成功的情况下不会返回。这些函数将用参数指定的新函数替换整个过程。这些功能仅在发生故障的情况下返回,并且可以从errno或通过perror(显示errno的描述)中读取错误。 the man page man execvp中定义了可能的错误。您应该在perror()之后使用exec*(),以打印出错误。

    您的输出始终为Command completed,因为您的execvp()调用总是失败。

  2. exec*函数家族不需要不需要了解有关“父”进程的任何信息(父进程实际上没有任何意义,因为它们取代了当前进程而不管它是什么)父母或子女)。唯一的参数是(取决于您使用的变体)新的过程路径,其参数和环境。

  3. 传递给main的argv参数将始终包含位置0的当前进程的名称,并且这些参数将紧随其后(直到argc - 1)。

  4. perror()函数接收一个字符串,然后打印该字符串,后跟冒号,错误说明和换行符。您应该perror("x"),而不是perror("x\n")

  5. 如果某件事失败并且您的程序无法继续,通常最好退出一个数字,使其与0 不同,以向调用方(或您,如果您正在外壳中运行它。

  6. exit()中使用main()是没有意义的,return将具有相同的效果,因为它是主程序。如果要从与exit()不同的函数中终止程序,则main()函数很有用。

  7. 据我所知,/Desktop/darren/lab01/c_ex1/a.out在普通UNIX系统上通常不是有效路径。通常,您的主目录中有Desktop。您可能要使用~/Desktop/...$HOME/Desktop/.../home/yourname/Desktop/...甚至是诸如./a.out之类的相对路径。

以上所述,您的代码的正确版本为:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc,char* argv[])
{
    unsigned counter;
    
    for(counter = 1; counter < argc; counter++) {
        pid_t pid = fork();
        if(pid < 0)
        {
            perror("fork failed");
            return 1;
        }
        else if(pid == 0)
        {
            char *args[] = {argv[counter],NULL};
            execvp(args[0],args);

            // OR,equivalent to the above two lines:
            // execlp(argv[counter],argv[counter],NULL);

            fprintf(stderr,"%s: ",argv[counter]);
            perror("execvp failed");
            return 1;
        }
    }

    return 0; 
}

示例输出:

$ ./cmdarguments ls pwd
cmdarguments x.c
/home/marco/test

$ ./cmdarguments /bin/ls /bin/pwd
cmdarguments x.c
/home/marco/test

$ ./cmdarguments /does/not/exist
/does/not/exist: execvp failed: No such file or directory