ls | wc使用C不起作用

我写了一个C程序,它使用多个管道来模拟shell。 问题是我可以运行大多数命令,如ls | cat ls | cat等,但我无法使用ls | wc ls | wc 。 什么情况下wc不起作用?

 int pipefd[4]; int p1 = pipe(pipefd); // Open pipe 1 int p2 = pipe(pipefd + 2); // Open pipe 2 pid_t pid; for(i = 0; i < n_commands; i++) { fflush(stdout); pid = fork(); if(pid == 0) { int command_no = i; int prev_pipe = ((command_no - 1) % 2) * 2; int current_pipe = (command_no % 2) * 2; // If current command is the first command, close the // read end, else read from the last command's pipe if(command_no == 0) { close(pipefd[0]); } else { dup2(pipefd[prev_pipe], 0); close(pipefd[current_pipe]); } // If current command is the last command, close the // write end, else write to the pipe if(command_no == n_commands - 1) { close(pipefd[current_pipe + 1]); } else { dup2(pipefd[current_pipe + 1], 1); } int p = execvp(tokens[cmd_pos[command_no]], tokens + cmd_pos[command_no]); close(pipefd[current_pipe]); close(pipefd[prev_pipe]); close(pipefd[prev_pipe + 1]); close(pipefd[current_pipe + 1]); _exit(0); } } 

似乎来自/usr/bin的程序如果不是管道中的第一个命令则不会被执行。

这是一个从您的代码创建的非常简单的程序 – 猜测如何创建管道并简化命令argv处理:

 #include  #include  #include  #include  #include  static char *argv_ls[] = { "ls", 0 }; static char *argv_wc[] = { "wc", 0 }; static char **cmds[] = { argv_ls, argv_wc }; int main(void) { int n_commands = 2; int pipefd[2]; pipe(&pipefd[0]); // Error check! fflush(stdout); for (int i = 0; i < n_commands; i++) { int pid = fork(); if (pid == 0) { int command_no = i; int prev_pipe = ((command_no - 1) % 2) * 2; int current_pipe = (command_no % 2) * 2; printf("cmd %d: prev pipe %d, curr pipe %d\n", i, prev_pipe, current_pipe); fflush(stdout); // If current command is the first command, close the // read end, else read from the last command's pipe if (command_no == 0) { close(pipefd[0]); } else { dup2(pipefd[prev_pipe], 0); close(pipefd[current_pipe]); // Line 40 } // If current command is the last command, close the // write end, else write to the pipe if (command_no == n_commands - 1) close(pipefd[current_pipe + 1]); // Line 46 else dup2(pipefd[current_pipe + 1], 1); execvp(cmds[i][0], cmds[i]); fprintf(stderr, "Failed to exec: %s (%d: %s)\n", cmds[i][0], errno, strerror(errno)); _exit(1); } } return 0; } 

当GCC 4.7.1(在Mac OS X 10.7.4上)编译它时,它会发出警告:

 pipes-12133858.c: In function 'main': pipes-12133858.c:40:22: warning: array subscript is above array bounds [-Warray-bounds] pipes-12133858.c:46:22: warning: array subscript is above array bounds [-Warray-bounds] 

当我运行它时,我得到输出:

 Isis JL: pipes-12133858 cmd 0: prev pipe -2, curr pipe 0 cmd 1: prev pipe 0, curr pipe 2 Isis JL: wc: stdin: read: Bad file descriptor 

由于代码中的父级不等待子级完成,因此提示出现在来自wc的错误消息之前,但是打印的诊断编号显示存在各种问题(编译器能够发现一些问题) )。

请注意,无需检查任何exec*()系列函数的返回值。 如果他们成功了,他们就不会回来; 如果他们回来,他们就失败了。 在调用_exit(0);之前也没有必要关闭_exit(0); 因为系统无论如何都会关闭它们。 此外,当您未能执行某些操作时,打印一条指示您未能执行的消息并以非零退出状态退出是有礼貌的。

因此,正如MichałGórny所说,问题的一个主要部分是你的管道处理代码至少是神秘的,因为你没有显示它并且可能是错误的。

我也很容易确定你的代码中没有足够的close()调用。 作为指导原则,在管道中打开管道的每个进程中, pipe()系统调用返回的所有文件描述符都应该在任何给定的子进程使用exec*()函数之前关闭。 。 不关闭管道会导致进程挂起,因为管道的写入端已打开。 如果打开写入结束的进程是尝试从管道的读取端读取的进程,那么它将不会找到要读取的任何数据。

您错误地连接了管道。

这个逻辑:

 int prev_pipe = ((command_no - 1) % 2) * 2; int current_pipe = (command_no % 2) * 2; 

不起作用 – 模数的结果将始终为01 ,因此prev_pipecurrent_pipe将为02

好吧,除非我错过了一些隐藏的概念,因为你没有粘贴创建管道的任何代码。