You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过传入的argv参数实现多命令管道(替代硬编码)

多命令管道从硬编码改为通过argv参数执行的实现方法

问题描述

我已经实现了多命令管道功能,但目前命令是硬编码形式。现希望改为通过传入的argv参数执行命令,参数以逗号分隔各命令(示例传入参数:echo "test" , wc , cowsay)。尝试过execve系列其他函数但无进展,考虑将argv整理为指针数组并在每个命令末尾添加NULL后遍历执行,寻求可行实现方法。

当前硬编码实现代码

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

void loop_pipe(char ***cmd);

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

    char *echo[] = {"echo", "test", NULL};
    char *rev[] = {"rev", NULL};
    char *wc[] = {"wc", NULL};
    char **cmd[] = {echo, wc, rev, NULL};

    loop_pipe(cmd);
    return (0);
}

void loop_pipe(char ***cmd) {
    int p[2];
    pid_t pid;
    int fd_in = 0;

    while (*cmd != NULL) {
        pipe(p);
        if ((pid = fork()) == -1) {
            exit(EXIT_FAILURE);
        } else if (pid == 0) {
            dup2(fd_in, 0); // 继承上一个命令的输出作为输入
            if (*(cmd + 1) != NULL)
                dup2(p[1], 1); // 非最后命令时将输出写入管道
            close(p[0]);
            execvp((*cmd)[0], *cmd);
            exit(EXIT_FAILURE);
        } else {
            wait(NULL);
            close(p[1]);
            fd_in = p[0]; // 保存管道读端作为下一个命令的输入
            cmd++;
        }
    }
}

解决方案

核心思路是解析argv参数,将逗号分隔的命令拆分为独立的参数列表,构建成和硬编码格式一致的char ***cmd结构,再传入原有的loop_pipe函数执行。具体实现步骤如下:

1. 解析argv参数

  • 遍历argv[1]到argv[argc-1],收集当前命令的参数;
  • 遇到参数为","时,结束当前命令收集,在参数列表末尾添加NULL,然后开始下一个命令;
  • 最后一个命令收集完成后,同样添加NULL,并在整个命令数组末尾添加NULL。

2. 动态内存分配

由于命令数量和每个命令的参数数量不确定,使用malloc/realloc动态分配内存,避免栈溢出。

3. 内存释放

使用完动态构建的命令数组后,逐层释放内存,避免内存泄漏。

完整实现代码

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

void loop_pipe(char ***cmd);
char ***parse_args(int argc, char *argv[]);
void free_cmd_array(char ***cmd);

int main(int argc, char *argv[]) {
    if (argc < 2) {
        fprintf(stderr, "Usage: %s <command1> , <command2> , ...\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    char ***cmd = parse_args(argc, argv);
    if (!cmd) {
        fprintf(stderr, "Failed to parse arguments\n");
        exit(EXIT_FAILURE);
    }

    loop_pipe(cmd);
    free_cmd_array(cmd);

    return 0;
}

char ***parse_args(int argc, char *argv[]) {
    char ***cmd_array = NULL;
    char **current_cmd = NULL;
    int cmd_count = 0;
    int arg_count = 0;

    for (int i = 1; i < argc; i++) {
        // 遇到逗号,结束当前命令
        if (strcmp(argv[i], ",") == 0) {
            if (current_cmd == NULL) {
                fprintf(stderr, "Invalid argument: consecutive commas\n");
                free_cmd_array(cmd_array);
                return NULL;
            }
            // 当前命令末尾加NULL
            current_cmd = realloc(current_cmd, sizeof(char *) * (arg_count + 1));
            current_cmd[arg_count] = NULL;
            // 将当前命令加入命令数组
            cmd_array = realloc(cmd_array, sizeof(char **) * (cmd_count + 1));
            cmd_array[cmd_count] = current_cmd;
            cmd_count++;
            // 重置当前命令的参数计数器
            current_cmd = NULL;
            arg_count = 0;
        } else {
            // 收集当前命令的参数
            current_cmd = realloc(current_cmd, sizeof(char *) * (arg_count + 1));
            current_cmd[arg_count] = argv[i];
            arg_count++;
        }
    }

    // 处理最后一个命令
    if (current_cmd != NULL) {
        current_cmd = realloc(current_cmd, sizeof(char *) * (arg_count + 1));
        current_cmd[arg_count] = NULL;
        cmd_array = realloc(cmd_array, sizeof(char **) * (cmd_count + 1));
        cmd_array[cmd_count] = current_cmd;
        cmd_count++;
    }

    // 命令数组末尾加NULL
    cmd_array = realloc(cmd_array, sizeof(char **) * (cmd_count + 1));
    cmd_array[cmd_count] = NULL;

    return cmd_array;
}

void free_cmd_array(char ***cmd) {
    if (!cmd) return;
    for (int i = 0; cmd[i] != NULL; i++) {
        free(cmd[i]);
    }
    free(cmd);
}

void loop_pipe(char ***cmd) {
    int p[2];
    pid_t pid;
    int fd_in = 0;

    while (*cmd != NULL) {
        if (pipe(p) == -1) {
            perror("pipe failed");
            exit(EXIT_FAILURE);
        }
        if ((pid = fork()) == -1) {
            perror("fork failed");
            exit(EXIT_FAILURE);
        } else if (pid == 0) {
            dup2(fd_in, 0);
            if (*(cmd + 1) != NULL)
                dup2(p[1], 1);
            // 关闭管道无用端
            close(p[0]);
            close(p[1]);
            execvp((*cmd)[0], *cmd);
            perror("execvp failed");
            exit(EXIT_FAILURE);
        } else {
            wait(NULL);
            close(p[1]);
            if (fd_in != 0) {
                close(fd_in); // 关闭上一个管道读端,避免资源泄漏
            }
            fd_in = p[0];
            cmd++;
        }
    }
    close(fd_in); // 关闭最后剩余的管道读端
}

说明

  • 编译运行:编译后执行./pipe_demo echo "hello world" , wc -l , cowsay即可实现多命令管道;
  • 输入校验:处理了连续逗号、无命令等非法输入情况;
  • 内存管理:新增free_cmd_array函数释放动态分配的内存,避免泄漏;
  • 函数优化:对原loop_pipe函数补充了文件描述符关闭逻辑,减少资源泄漏风险。

内容的提问来源于stack exchange,提问作者LIsa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 20:20:03