如何让 execvp 函数执行每个命令行参数?

How can I make the execvp function execute each command line argument?

我正在尝试编写一个程序,该程序在执行它们之前从同一行输入(例如 /bin/uname /bin/date)获取多个命令行参数并指示该过程已成功完成。当我使用无限循环时,程序会完美地打印出每次执行,但是问题规定父进程必须在所有子进程终止后终止。尝试实现这一点只会导致一个进程完成,尽管事实上它们是一个应该允许它重复的 for 循环。任何帮助告诉我应该如何去做的人都会很棒。

我已经尝试将 for 循环移动到各个不同的位置,以及删除退出调用以尝试使其成功循环

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>

#define KGRN "\x1B[32m"
#define RESET "\x1B[0m"

int main(int argc,char* argv[]) 
{ 
    int counter;
    int status;
    int COMMAND_LINE_SIZE = 64;
    char command[COMMAND_LINE_SIZE];

    pid_t pid;

    printf("$ ");
    scanf("%s", command);//read a line from user and store it in array command
    printf("\n");

    for(counter=0;counter<argc;counter++){
        if ((pid = fork()) <  0) {
            perror("forking child process failed\n");
            exit(1); 
        }

        if (pid==0){ // child
            char *argv[] = {command, NULL};
            execvp(argv[counter], argv);
            exit(0);
        }
        //in parent
        pid = wait(&status);
        if(WIFEXITED(status) == 1)
            printf(KGRN "Command %s has completed successfully\n" RESET, command);
        else
            printf("failure\n");
    }
}

预期输出:

$ /bin/uname /bin/date /bin/ls
Linux  
Command /bin/uname has completed successfully
$ 
Sun Sep 15 12:24:39 UTC 2019
Command /bin/date has completed successfully
$ 
a.out  new.c  q3.c  trial.c
Command /bin/ls has completed successfully

当前输出:

$ /bin/ls /bin/uname

a.out  new.c  q3.c  trial.c
Command /bin/ls has completed successfully

你很接近,但是有很多问题,其中很多问题在问题的评论中指出:

  • 你 re-execute argv[0] — 程序 — 这预示着不好。
  • 您通常应该在分叉 children 的循环中执行 child 的操作。
  • 如果你想让 children 并发执行,你需要 wait() 在分叉它们的循环之外调用。
  • 尽管规范说要执行命令行参数,您还是从标准输入读取了命令。
  • 您使用从标准输入读取的数据作为已执行进程的命令名称,而不是命令行参数。
  • 您有一个局部变量 argv 隐藏(隐藏)main()argv 参数。因此,execvp() 中的代码在 counter 达到 2 后越界访问 argv 数组,并且当 counter 为 1 时使用空指针。
  • 在 child 执行失败后,您不应报告成功 (exit(0);)。
  • 我认为当 child 执行失败时,您应该报告哪个进程执行失败——当然是标准错误。

顺便说一句,您的示例命令行都使用命令的完整路径,因此不需要使用 execvp()。您完全可以只键入命令的名称,就像在 shell 提示符下一样,然后 execvp() 会找到该命令并 运行 它(参见我的示例 运行s).

异步执行

修复这些问题并添加一些小的调整,然后 运行 异步地调用命令行参数(可能同时 运行 调用许多命令)产生如下代码:

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    for (int counter = 1; counter < argc; counter++)
    {
        pid_t pid = fork();
        if (pid <  0)
        {
            perror("forking child process failed\n");
            exit(EXIT_FAILURE);
        }
        else if (pid == 0) // child
        {
            char *args[] = { argv[counter], NULL };
            execvp(args[0], args);
            fprintf(stderr, "%s: failed to execute %s: (%d) %s\n",
                    argv[0], argv[counter], errno, strerror(errno));
            exit(EXIT_FAILURE);
        }
    }

    int corpse;
    int status;
    int failed = 0;
    while ((corpse = waitpid(0, &status, 0)) > 0)
    {
        if (WIFEXITED(status))
        {
            printf("Process %d exited with status %d (0x%.4X)\n",
                   corpse, WEXITSTATUS(status), status);
            if (WEXITSTATUS(status) != 0)
                failed++;
        }
        else if (WIFSIGNALED(status))
        {
            printf("Process %d was signalled %d (0x%.4X)\n",
                   corpse, WTERMSIG(status), status);
            failed++;
        }
    }
    return((failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
}

failed 变量跟踪是否有任何命令失败,如果每个命令都成功,则进程仅报告成功(以 0 或 EXIT_SUCCESS 退出);否则,它将以失败指示退出(以 EXIT_FAILURE 退出,通常为 1)。 当 运行 在我的 Mac 上(因为 args41args41.c 创建),它产生:

$ args41 uname date ls
Sun Sep 15 07:26:58 MDT 2019
Darwin
Process 1907 exited with status 0 (0x0000)
Process 1908 exited with status 0 (0x0000)
20000-leagues-under-the-sea.txt bin                             maxargs.sh
LICENSE.md                      conn11                          overlap.data
Metriseis_2012.dat              conn11.c                        overlap47.c
README.md                       conn11.dSYM                     overlap61.c
Safe                            conn13                          overlap73
Untracked                       conn13.c                        overlap73.c
acr.list                        conn13.dSYM                     overlap73.dSYM
acronym29.c                     conn17                          packages
acronym43                       conn17.c                        pseudo-json.md
acronym43.c                     conn17.dSYM                     question.md
acronym43.dSYM                  conn29                          sll43.c
acronym47                       conn29.c                        so-0167-2112
acronym47.c                     conn29.dSYM                     so-0167-2112.c
acronym47.dSYM                  doc                             so-0167-2112.dSYM
acronym53                       dr41                            so-1043-1305
acronym53.c                     dr41.c                          so-4921-8019
acronym53.dSYM                  dr41.dSYM                       so-4970-8730
acronym59                       dw41                            so-4971-1989
acronym59.c                     dw41.c                          so-4985-0919
acronym59.dSYM                  dw41.dSYM                       so-5102-0102
argc37                          etc                             so-5134-1743
argc37.c                        gccw67                          so-5225-1783
argc37.dSYM                     gccw67.c                        so-5279-4924
args41                          gccw67.dSYM                     so-5358-5962
args41.c                        gccw67.o                        so-5394-5215
args41.dSYM                     get.jl.activity                 so-5416-6308
argv89                          inc                             so-5424-4465.md
argv89.c                        lib                             src
argv89.dSYM                     makefile
Process 1909 exited with status 0 (0x0000)
$ args41 many things to do
args41: failed to execute many: (2) No such file or directory
args41: failed to execute things: (2) No such file or directory
args41: failed to execute to: (2) No such file or directory
args41: failed to execute do: (2) No such file or directory
Process 1939 exited with status 1 (0x0100)
Process 1941 exited with status 1 (0x0100)
Process 1940 exited with status 1 (0x0100)
Process 1942 exited with status 1 (0x0100)
$

显然,如果您希望进程 运行 同步,您应该将 waitpid() 循环放在 for 循环中。您仍然应该使用循环,因为进程可以继承 children 它在模糊的情况下没有分叉。在这种情况下,您可能更愿意使用 waitpid(pid, &status, 0);那么你不需要围绕 waitpid().

的循环

如果您想跟踪进程名称但 运行 异步进程,则 parent 进程需要在数组中记录每个 child 的 PID当他们被分叉时,报告循环将搜索已知 children 的列表以报告哪一个死亡。

同步执行

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    int failed = 0;

    for (int counter = 1; counter < argc; counter++)
    {
        pid_t pid = fork();
        if (pid <  0)
        {
            perror("forking child process failed\n");
            exit(EXIT_FAILURE);
        }
        else if (pid == 0) // child
        {
            char *args[] = { argv[counter], NULL };
            execvp(args[0], args);
            fprintf(stderr, "%s: failed to execute %s: (%d) %s\n",
                    argv[0], argv[counter], errno, strerror(errno));
            exit(EXIT_FAILURE);
        }
        else
        {
            int status;
            if (waitpid(pid, &status, 0) < 0)
            {
                fprintf(stderr, "failed to wait for %s process %d: (%d) %s\n",
                        argv[counter], (int)pid, errno, strerror(errno));
                failed++;
            }
            else if (WIFEXITED(status))
            {
                printf("Process %s exited with status %d (0x%.4X)\n",
                        argv[counter], WEXITSTATUS(status), status);
                if (WEXITSTATUS(status) != 0)
                    failed++;
            }
            else if (WIFSIGNALED(status))
            {
                printf("Process %s was signalled %d (0x%.4X)\n",
                        argv[counter], WTERMSIG(status), status);
                failed++;
            }
            else
            {
                printf("Process %s died unexpectedly (0x%.4X)\n",
                        argv[counter], status);
                failed++;
            }
        }
    }

    return((failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
}

示例输出(args89args89.c 创建):

$ args89 date uname ls
Sun Sep 15 08:34:00 MDT 2019
Process date exited with status 0 (0x0000)
Darwin
Process uname exited with status 0 (0x0000)
20000-leagues-under-the-sea.txt argv89.c                        makefile
LICENSE.md                      argv89.dSYM                     maxargs.sh
Metriseis_2012.dat              bin                             overlap.data
README.md                       conn11                          overlap47.c
Safe                            conn11.c                        overlap61.c
Untracked                       conn11.dSYM                     overlap73
acr.list                        conn13                          overlap73.c
acronym29.c                     conn13.c                        overlap73.dSYM
acronym43                       conn13.dSYM                     packages
acronym43.c                     conn17                          pseudo-json.md
acronym43.dSYM                  conn17.c                        question.md
acronym47                       conn17.dSYM                     sll43.c
acronym47.c                     conn29                          so-0167-2112
acronym47.dSYM                  conn29.c                        so-0167-2112.c
acronym53                       conn29.dSYM                     so-0167-2112.dSYM
acronym53.c                     doc                             so-1043-1305
acronym53.dSYM                  dr41                            so-4921-8019
acronym59                       dr41.c                          so-4970-8730
acronym59.c                     dr41.dSYM                       so-4971-1989
acronym59.dSYM                  dw41                            so-4985-0919
argc37                          dw41.c                          so-5102-0102
argc37.c                        dw41.dSYM                       so-5134-1743
argc37.dSYM                     etc                             so-5225-1783
args41                          gccw67                          so-5279-4924
args41.c                        gccw67.c                        so-5358-5962
args41.dSYM                     gccw67.dSYM                     so-5394-5215
args89                          gccw67.o                        so-5416-6308
args89.c                        get.jl.activity                 so-5424-4465.md
args89.dSYM                     inc                             src
argv89                          lib
Process ls exited with status 0 (0x0000)
$

带有进程名称跟踪的异步执行

此变体 运行 以异步方式发送命令,但也跟踪属于每个 PID 的进程。请注意,它使用一个函数 (shock, horror) 来确定与刚刚死亡的 PID 对应的参数。设置数组以便于跟踪数字 — 未使用 PID 数组的元素 0。

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

static int find_pid(int pid, int num_pids, pid_t pids[num_pids])
{
    for (int i = 1; i < num_pids; i++)
    {
        if (pids[i] == pid)
            return i;
    }
    return -1;
}

int main(int argc, char *argv[])
{
    pid_t pids[argc];
    for (int counter = 1; counter < argc; counter++)
    {
        pid_t pid = fork();
        if (pid <  0)
        {
            perror("forking child process failed\n");
            exit(EXIT_FAILURE);
        }
        else if (pid == 0) // child
        {
            char *args[] = { argv[counter], NULL };
            execvp(args[0], args);
            fprintf(stderr, "%s: failed to execute %s: (%d) %s\n",
                    argv[0], argv[counter], errno, strerror(errno));
            exit(EXIT_FAILURE);
        }
        else
            pids[counter] = pid;
    }

    int corpse;
    int status;
    int failed = 0;
    while ((corpse = waitpid(0, &status, 0)) > 0)
    {
        int index = find_pid(corpse, argc, pids);
        if (index < 0)
        {
            fprintf(stderr, "Unrecognized PID %d exited with status 0x%.4X\n",
                    corpse, status);
            failed++;
        }
        else if (WIFEXITED(status))
        {
            printf("Process %s (PID %d) exited with status %d (0x%.4X)\n",
                   argv[index], corpse, WEXITSTATUS(status), status);
            if (WEXITSTATUS(status) != 0)
                failed++;
        }
        else if (WIFSIGNALED(status))
        {
            printf("Process %s (PID %d) was signalled %d (0x%.4X)\n",
                   argv[index], corpse, WTERMSIG(status), status);
            failed++;
        }
        else
        {
            printf("Process %s (PID %d) died from indeterminate causes (0x%.4X)\n",
                   argv[index], corpse, status);
            failed++;
        }
    }
    return((failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
}

示例 运行(args79args79.c 创建):

$ args79 uname date pwd
/Users/jleffler/soq
Process pwd (PID 2105) exited with status 0 (0x0000)
Darwin
Sun Sep 15 09:04:37 MDT 2019
Process uname (PID 2103) exited with status 0 (0x0000)
Process date (PID 2104) exited with status 0 (0x0000)
$