ls | wc с использованием C не работает

Я написал программу на C, которая использует несколько каналов для моделирования оболочки. Проблема в том, что я могу запускать большинство команд, таких как 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); так как система все равно их закроет. Кроме того, если вы не выполняете что-то, то вежливо напечатать сообщение о том, что вы не смогли выполнить, и выйти с ненулевым статусом выхода.

Итак, как говорит Michał Górny , основная часть вашей проблемы заключается в том, что ваш код обработки труб, по крайней мере, таинственный, потому что вы его не показывали и, вероятно, ошибочно.

Я также с уверенностью уверен, что у вас недостаточно вызовов close() в вашем коде. В качестве ориентира в каждом процессе, в котором открыты трубы, которые будут частью конвейера, все дескрипторы файлов, возвращаемые системным вызовом pipe() должны быть закрыты до того, как любой дочерний процесс использует функцию exec*() , Не закрывающиеся трубы могут привести к зависанию процессов, так как конец записи трубы открыт. Если процесс, в котором открыт конец записи, - это процесс, который пытается прочитать из прочитанного конца канала, тогда он не найдет никаких данных для чтения.

Вы неправильно соединяете трубы.

Эта логика:

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

не работает – результат по модулю всегда будет 0 или 1 , поэтому prev_pipe и current_pipe будут либо 0 либо 2

Ну, если мне не хватает какой-то скрытой концепции, потому что вы не вставляете код, создающий трубы.