首页 > 解决方案 > 使用 execvp() 分离和执行命令行参数

问题描述

下面的代码可以正常工作。我输入任意 2 个命令,在本例中为 ls 和 pwd,终端输出这些命令的结果,以及运行它的子进程的 PID。

命令行目前看起来像这样:./practice.c ls pwd

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

int main(int argc, char ** argv){

   long cpid1, cpid2;

   switch(cpid1 = fork()){
      case -1:
         perror("fork failed");
         return EXIT_FAILURE;

      case 0:
         printf("First child, pid=%d, executing command...\n", getpid());
         execvp(argv[1], NULL);
         return EXIT_FAILURE;
   }

   wait(NULL);
   switch(cpid2 = fork()){
      case -1:
         perror("fork failed");
         return EXIT_FAILURE;
      
      case 0:
         printf("Second child, pid=%d, executing command...\n", getpid());
         execvp(argv[2], NULL);
         return EXIT_FAILURE;
   }

   while(wait(NULL) != -1);
   return EXIT_SUCCESS;

}

但是,我希望用户输入一个看起来更像这样的命令行: ./practice.c pwd , ls -l

所以,首先我的问题是分离出命令行。“PWD”将是 argv[1],然后“,”将是 argv[2],然后“ls”将是 argv[3],依此类推。我真正想要的是使用逗号作为分隔符。即便如此,在“ls -l”的情况下,我希望它是 1 个 arg,所以当我运行 exec 时,它看起来就像我所做的只是运行 execvp(“ls”,“-l”,NULL)。你有什么建议或技巧吗?

标签: cforkcommand-line-argumentsexecvp

解决方案


将一些评论转移到答案中。

请注意,您会有类似的东西:

char *args[3] = { "ls", "-l", NULL };

(使用从 — 推导出的值argv,您需要args[]根据仅包含逗号 ( ) 的参数之前或之后的参数数量来调整大小,),然后调用execvp(args[0], args);. 您为调用显示的符号execvp()将适用于execlp(),但仅当您知道编译时的参数数量时。在您修改后的要求中,您不知道编译时参数的数量,这就是您需要使用execvp().

您现有的代码没有为调用的程序提供任何参数。不建议这样做。同样,您应该创建一个数组并使用它:

char args[2] = { argv[1], NULL };
…
execvp(args[0], args);

当然 forargv[2]和第二个命令也是如此。根据修改后的要求,您需要将参数累积到逗号参数,然后运行它。您可以找到逗号参数的索引并将其替换为 NULL 并从那里开始工作。

此代码将执行提取到一个函数中execute(),让main()函数拆分参数列表等。

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

static void execute(char **args);

int main(int argc, char **argv)
{
    int arg1 = 1;
    for (int i = 1; i < argc; i++)
    {
        if (strcmp(argv[i], ",") == 0)
        {
            argv[i] = NULL;
            execute(&argv[arg1]);
            arg1 = i + 1;
        }
    }
    if (argv[arg1] != NULL)
        execute(&argv[arg1]);

    return EXIT_SUCCESS;
}

static void execute(char **args)
{
    pid_t pid;
    if ((pid = fork()) == -1)
    {
        perror("fork failed");
        exit(EXIT_FAILURE);
    }
    else if (pid == 0)
    {
        char **argv = args;
        while (*argv)
            printf("[%s]", *argv++);
        putchar('\n');
        printf("Child pid=%d executing command %s...\n", getpid(), args[0]);
        execvp(args[0], args);
        fprintf(stderr, "%d: failed to execute command %s\n", getpid(), args[0]);
        exit(EXIT_FAILURE);
    }
    else
    {
        int corpse;
        int status;
        while ((corpse = wait(&status)) != -1)
            printf("Child %d exited with status 0x%.4X\n", corpse, status);
    }
}

程序源在vp13.c;程序是vp13。当像这样运行时,它会产生:

$ vp13 ls -l , pwd , ls -C -F , date '+%Y-%m-%dT%H:%M:%S'
[ls][-l]
Child pid=54602 executing command ls...
total 32
-rwxr-xr-x  1 jonathanleffler  staff  9076 Dec  3 21:18 vp13
-rw-r--r--  1 jonathanleffler  staff  1148 Dec  3 21:18 vp13.c
drwxr-xr-x  3 jonathanleffler  staff    96 Dec  3 21:15 vp13.dSYM
Child 54602 exited with status 0x0000
[pwd]
Child pid=54603 executing command pwd...
/Users/jonathanleffler/soq/so-6513-7710
Child 54603 exited with status 0x0000
[ls][-C][-F]
Child pid=54604 executing command ls...
vp13*      vp13.c     vp13.dSYM/
Child 54604 exited with status 0x0000
[date][+%Y-%m-%dT%H:%M:%S]
Child pid=54605 executing command date...
2020-12-03T21:23:55
Child 54605 exited with status 0x0000
$

我相信这对你来说不太灵活。


推荐阅读