[英]ls | wc using C doesn't work
我寫了一個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
中的程序似乎沒有被執行。
您錯誤地連接了管道。
這個邏輯:
int prev_pipe = ((command_no - 1) % 2) * 2;
int current_pipe = (command_no % 2) * 2;
不起作用 - 模數的結果將始終為0
或1
,因此prev_pipe
和current_pipe
將為0
或2
...
好吧,除非我錯過了一些隱藏的概念,因為你沒有粘貼創建管道的任何代碼。
這是一個從您的代碼創建的非常簡單的程序 - 猜測如何創建管道並簡化命令argv
處理:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
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*()
函數之前關閉。 。 不關閉管道會導致進程掛起,因為管道的寫入端已打開。 如果打開寫入結束的進程是嘗試從管道的讀取端讀取的進程,那么它將不會找到要讀取的任何數據。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.