【问题标题】:How to execute multiple commands from argv with an specific delimiter如何使用特定分隔符从 argv 执行多个命令
【发布时间】:2022-01-02 04:29:56
【问题描述】:

最近我有一个任务,我必须编写一个程序,从命令行获取两个不同的命令,用“+”分隔,例如:

ps -lu myUsername + ls -la

该程序的目标是使用fork()exec() 同时运行任意两个订单,每个订单有任意数量的参数。这是我对这个问题的解决方案:

注意:最初的问题是在具有 Solaris 5.10 和 c89 或 c99 标准 (gcc 3.4.3) 的机器上运行的

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <strings.h>

int main (int argc, char* argv[]) {
    char delimiter = '+';
    char* auxp; //auxiliar pointer
    int i = 1;
    int position;
    
    while(i < argc){
        if (strcmp("+", argv[i]) == 0) {
            argv[i] = NULL;
            position = i;
        }
        i++;
    }

    if (fork() == 0) {
        execvp(argv[1], &argv[1]);
        exit(1);
    }

    if (fork() == 0) {
        execvp(argv[position+1], &argv[position+1]);
        exit(1);
    }

    wait(NULL);
    wait(NULL);
    exit(0);
}

这对于分配来说已经足够了,但我想让它使用 N 个参数 而不是只有 2 个。我无法找到系统的方法来查找所有地址。任何帮助表示赞赏,感谢您的建议。

【问题讨论】:

  • i&lt;argc 。创建一个临时char **myargv。每当遇到 + 时,将每个命令复制到 myargv。分叉并执行。

标签: c fork exec command-line-arguments argv


【解决方案1】:

只需将分叉移入循环:

int main (int argc, char* argv[])
{
    char** start = ++argv;
    unsigned int n = 0;
    for(; *argv; ++argv) // profiting from argv being null terminated, too...
    {
        if (strcmp("+", *argv) == 0)
        {
            *argv = NULL;
            if (fork() == 0)
            {
                execvp(*start, start);
                exit(1);
            }
            start = argv + 1;
            ++n; // but need to count how many times we actually forked!
        }
    }

    while(n--)
    {
        wait(NULL);
    }
    exit(0);
}

好的,我也对迭代进行了一些修改——指针更好(个人意见...)。

注意:这是未经测试的代码,如果发现错误请自行修复...

【讨论】:

  • 感谢您的回复,您能否向我解释一下for(; *argv, argv) 的迭代是如何工作的?我是 C 新手,从未见过这样的 for 循环。
  • @sosensio for 循环包含三个表达式,其中任何一个都可以保持为空:for(;;) 是另一种编写无限循环的方法。所以我不会为初始化做任何事情,然后检查*argv 是否为空(就像if(x):任何不为0 的值都被解释为true - 并且指针实际上是内存地址和因此也只是普通的整数值),最后增加argv(注意:它是++argv)。这是因为 argv 以 null 结尾(即最后一个元素后面跟着一个 null pointer,就像 null character 字符串一样)。
  • 啊,对于你根本不知道 for 循环的情况:for(X; Y; Z) 其中X 初始化循环,该部分在进入循环体之前执行一次并且可以包含变量声明也是如此,Y 是循环测试,在 每个 循环运行之前检查是否应该输入主体,Z 是每次循环执行的后操作已经运行,所以循环体之后。经典:for(int i = 0; i &lt; n; ++i) {...}。一个好的C book 应该很快就会介绍给你。
【解决方案2】:

对于 N 个命令的一般情况,需要跟踪每个命令的开始和结束位置,并在找到命令结束时分叉一个新子项。这可能在 + 分隔符参数处,或者在原始参数列表的末尾。

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

int main (int argc, char* argv[]) {
    /*
     * Note: argc could be in range 0 to INT_MAX.
     * Using unsigned int values to avoid arithmetic overflow.
     */
    unsigned int start = 1;
    unsigned int end = 1;
    unsigned int child_count = 0;

    /* Note: argv[argc] is a terminating null pointer. */
    while(end <= (unsigned int)argc){
        if(end == (unsigned int)argc || strcmp("+", argv[end]) == 0){
            /* Reached the terminating null pointer or a command separator. */
            argv[end] = NULL;
            if(start != end){
                /*
                 * Command is not empty.
                 * Fork a child process to execute the command.
                 */
                pid_t child = fork();
                if(child > 0){
                    /* Parent forked child successfully. */
                    child_count++;
                }else if(child == 0){
                    /* This is the child process. Execute command. */
                    execvp(argv[start], &argv[start]);
                    exit(1);
                }
            }
            /* Next command starts after this one. */
            start = end + 1;
        }
        /* Looking for terminating null pointer or command separator. */
        end++;
    }

    /* Wait for the child processes to terminate. */
    while(child_count){
        wait(NULL);
        child_count--;
    }
    exit(0);
}

注意:argv[end] = NULL; 行可以移动到 if(child == 0){ } 块中(但在调用 execvp 之前)以保持父级的原始参数列表不变。

【讨论】:

    猜你喜欢
    • 2022-01-06
    • 2015-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-22
    • 2017-11-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多