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

标签 c ubuntu unix exec execvp

我正在尝试编写一个程序,在执行它们之前从同一行输入(例如/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

最佳答案

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

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

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

异步执行

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

#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 上运行时(作为从 args41.c 创建的 args41),它产生:

$ 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 循环中。您仍然应该使用循环,因为一个进程可以继承它在模糊情况下没有 fork 的子进程。在这种情况下,您可能更愿意使用 waitpid(pid, &status, 0);那么你不需要围绕 waitpid() 的循环。

如果您想跟踪进程名称并异步运行进程,那么父进程需要在数组中记录每个子进程的 PID,因为它们被 fork 出来,报告循环将搜索已知 child 的名单,以报告哪一个死亡。

同步执行

#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)
$

关于c - 如何让 execvp 函数执行每个命令行参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57944117/

相关文章:

javascript - nodejs helloworld 测试应用程序未运行

ubuntu - 使用 terraform 将 ubuntu iso 部署到 proxmox

bash - 如何使用 "find"通过正则表达式搜索文件名

c - 在 1 行中将字符串发送到标准输出和套接字的方法

c# - 如何在 C 中初始化多维字符数组?

c - pthread 程序中例程的额外执行时间花费了什么?

c++ - 我应该在 dlopen 之前锁定吗?

unix - 如何设置RabbitMQ目录创建新文件的权限?

linux - 在 fork() 之前阻塞 SIGCHLD 的目的是什么?

c - 一个 friend 给我发了一段我不明白的片段。这是如何运作的?