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

C语言简易Shell命令管道实现异常:部分多管道命令执行失败

简易Shell管道功能异常问题

我正在用C语言编写类Bash的简易Shell,目前实现命令间的管道功能(即command1 | command2,需要同时运行两个命令,将前一个命令的stdout通过管道连接到后一个命令的stdin)。

目前类似如下简单命令可以正常执行并输出结果:

shell> echo test | cat | cat

该命令能正确打印"test",但更复杂的命令却无法正常运行,例如:

shell> ls -1 / | sort | rev

从管道逻辑而言,该命令和前者并无差异,但前者成功而后者失败。我已经对主进程和子进程进行了全面调试,确认工作和不工作的命令中,进程都以正确的连接方式启动,但仍完全找不到问题原因。

以下是简化后的代码:

// Uncomment to use hardcoded input
// #define USE_HARDCODED_INPUT

#include <stdlib.h>
#include <string.h>
#include <stddef.h>         // NULL
#include <errno.h>          // ENOENT
#include <stdio.h>          // setbuf, printf
#include <unistd.h>         // exec, fork
#include <fcntl.h>          // open
#include <sys/types.h>      // wait
#include <sys/wait.h>

void set_process_FDs(int input, int output, int error)
{
    if (input)
    {
        dup2(input, STDIN_FILENO);
        close(input);
    }

    if (output)
    {
        dup2(output, STDOUT_FILENO);
        close(output);
    }

    if (error)
    {
        dup2(error, STDERR_FILENO);
        close(error);
    }
}

void child_setup(char **argv, int input, int output, int error)
{
    if (input || output || error)
        set_process_FDs(input, output, error);

    execvp(argv[0], argv);

    perror("exec()");
    exit(1);
}

int launch_process(char **argv, int is_last,
                   int input, int output, int error)
{
    int status;

    pid_t pid = fork();

    switch(pid)
    {
        case -1:
            perror("fork()");
            return 0;
        case 0:
            child_setup(argv, input, output, error);
            return 0;
        default:
            break;
    }

    if (is_last)
        wait(&status);

    return 1;
}

int run_commands(char ***argvv)
{
    int no_commands_ran = 0;

    int argc;
    char **argv = argvv[0];

    int in_pipe[2];
    int out_pipe[2];

    for (int i=0; (argv = argvv[i]); ++i)
    {
        pipe(out_pipe);
        
        if (i == 0)
            in_pipe[0] = 0;

        if (!argvv[i+1])
        {
            close(out_pipe[0]);
            close(out_pipe[1]);

            out_pipe[1] = 0;
        }

        for (argc=0; argv[argc]; ++argc);
        if (!launch_process(argv, !argvv[i+1],
                            in_pipe[0], out_pipe[1], 0))
            break;

        if (i != 0)
        {
            close(in_pipe[0]);
            close(in_pipe[1]);
        }

        in_pipe[0] = out_pipe[0];
        in_pipe[1] = out_pipe[1];

        no_commands_ran = i + 1;
    }

    return no_commands_ran;
}

extern int obtain_order();  // Obtains an order from stdin

int main(void)
{
    char ***argvv = NULL;
    int argvc;

    char *filev[3] = {NULL, NULL, NULL};
    int bg;

    int ret;

    setbuf(stdout, NULL);       // Unbuffered
    setbuf(stdin, NULL);

    while (1)
    {
#ifndef USE_HARDCODED_INPUT
        printf("%s", "shell> ");     // Prompt

        ret = obtain_order(&argvv, filev, &bg);

        if (ret == 0)       // EOF
        {
            fprintf(stderr, "EOF\n");
            break;
        }

        if (ret == -1)
            continue;       // Syntax error

        argvc = ret - 1;    // Line
        if (argvc == 0)
            continue;       // Empty line

        if (!run_commands(argvv))
            continue;       // Error executing command

#else
        argvc = 3;

        char ***argvv1 = calloc(4, sizeof(char*));
        argvv1[0] = calloc(3, sizeof(char*));
        argvv1[0][0] = strdup("echo");
        argvv1[0][1] = strdup("test");
        argvv1[1] = calloc(2, sizeof(char*));
        argvv1[1][0] = strdup("cat");
        argvv1[2] = calloc(2, sizeof(char*));
        argvv1[2][0] = strdup("cat");

        char ***argvv2 = calloc(4, sizeof(char*));
        argvv2[0] = calloc(4, sizeof(char*));
        argvv2[0][0] = strdup("ls");
        argvv2[0][1] = strdup("-1");
        argvv2[0][2] = strdup("/");
        argvv2[1] = calloc(4, sizeof(char*));
        argvv2[1][0] = strdup("sort");
        argvv2[2] = calloc(4, sizeof(char*));
        argvv2[2][0] = strdup("rev");

        printf("%s", "shell> echo test | cat | cat\n");
        if (!run_commands(argvv1))
            continue;       // Error executing command

        usleep(500);

        printf("%s", "shell> ls -1 / | sort | rev\n");
        if (!run_commands(argvv2))
            continue;       // Error executing command

        printf("%s", "\nNo more hardcoded commands to run\n");
        break;
#endif
    }

    return 0;
}

obtain_order()是位于Yacc简易解析器中的函数,用于将Shell输入内容填充到argvv这个argv向量中。如果想要复现问题,只需取消注释代码开头的#define宏定义,即可看到手动输入问题命令时的表现。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 20:05:47