使用循环在 C 中管道两个或多个 shell 命令

Posted

技术标签:

【中文标题】使用循环在 C 中管道两个或多个 shell 命令【英文标题】:Pipe two or more shell commands in C using a loop 【发布时间】:2021-12-22 23:15:19 【问题描述】:

我正在尝试通过 C 中的程序执行ls | wc -l,而不是使用命令行。 这是我当前的工作代码:

int main()  
   int pfds[2]; 
   pipe(pfds); 
   pid_t pid = fork(); 
   if ( pid == 0 )  /* The child process*/ 
      close(1);
      dup(pfds[1]);
      close(pfds[0]);
      execlp("ls", "ls", NULL); 
    else  /* The parent process*/ 
      close(0);
      dup(pfds[0]);
      close(pfds[1]);
      wait(0);
      execlp("wc", "wc", "-l", NULL); 
    
   return 0; 

如何重写此代码以使用 for 循环?

例如:

for (i=0; i<2; i++) 

    // Rewrite the 2-level pipe here


稍后,我想扩展 for 循环以执行更多进程,例如 a | b | c | ...

【问题讨论】:

您想使用for 循环运行ls | wc -l N 次而不是一次(在您的示例2 中)? 对于简单的双命令管道没关系,您是否尝试为未知长度的更通用管道解决这个问题? @MarcoBonelli 是的,我正在尝试解决这个问题以获得更通用的管道 @HMemon 我想你误解了我的问题。我在问您是否要多次运行完全相同的管道进程 (ls | wc -l)。您想要那个,还是想要使用for 循环来执行更多 个进程,如a | b | c | ...?不清楚你在问什么。您应该在问题中指定这一点。 @MarcoBonelli,我想使用 for 循环来执行更多的进程,就像 |乙 | c | ... 【参考方案1】:

为了将多个命令连接在一起,您需要让父级保持运行以保持每个命令的fork()ing。

使用for 循环,您需要对第一个n - 1 命令执行此操作(最后一个将在主程序中执行):

    创建管道。 执行fork()。 在子进程中:用上一个管道的读端覆盖标准输入,用当前管道的写端覆盖标准输出。 在孩子中:执行execve()。 在父级中:关闭不需要的管道并保存当前管道的读取端以用于下一次迭代。

然后,循环结束后,用最后一个管道的读取端覆盖标准输入,并执行最后一个命令的execve()


下面我写了一个简单的执行示例:

ls | wc -l | xargs printf "0x%x\n" | cowsay

它应该适用于任意数量的命令(仅包括 1 个单一命令)。

注意:为了简短起见,我没有在此代码中为 execvp() 添加错误检查,但您绝对应该在每次调用 pipe()dup2() 后检查错误, fork() 和任何其他函数。

代码:

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

#define MAX_ARGC 3

int main(void) 
    char *commands[][MAX_ARGC + 1] = 
        "ls", NULL,
        "wc", "-l", NULL,
        "xargs", "printf", "0x%x\n", NULL,
        "cowsay", NULL
    ;

    size_t i, n;
    int prev_pipe, pfds[2];

    n = sizeof(commands) / sizeof(*commands);
    prev_pipe = STDIN_FILENO;

    for (i = 0; i < n - 1; i++) 
        pipe(pfds);

        if (fork() == 0) 
            // Redirect previous pipe to stdin
            if (prev_pipe != STDIN_FILENO) 
                dup2(prev_pipe, STDIN_FILENO);
                close(prev_pipe);
            

            // Redirect stdout to current pipe
            dup2(pfds[1], STDOUT_FILENO);
            close(pfds[1]);

            // Start command
            execvp(commands[i][0], commands[i]);

            perror("execvp failed");
            exit(1);
        

        // Close read end of previous pipe (not needed in the parent)
        close(prev_pipe);

        // Close write end of current pipe (not needed in the parent)
        close(pfds[1]);

        // Save read end of current pipe to use in next iteration
        prev_pipe = pfds[0];
    

    // Get stdin from last pipe
    if (prev_pipe != STDIN_FILENO) 
        dup2(prev_pipe, STDIN_FILENO);
        close(prev_pipe);
    

    // Start last command
    execvp(commands[i][0], commands[i]);

    perror("execvp failed");
    exit(1);

我机器上的输出(因为 ls 返回 41 == 0x29 行):

 ______
< 0x29 >
 ------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

【讨论】:

以上是关于使用循环在 C 中管道两个或多个 shell 命令的主要内容,如果未能解决你的问题,请参考以下文章

shell 管道命令与过滤器

24 shell 管道命令与过滤器

Shell while循环语句中的陷阱

shell第四篇(上)

在shell中用管道连接n个命令?

C++ Shell 多管道不工作?