这是我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
#include <readline/readline.h>

#define NUMPIPES 2

int main(int argc, char *argv[]) {
    char *bBuffer, *sPtr, *aPtr = NULL, *pipeComms[NUMPIPES], *cmdArgs[10];
    int fdPipe[2], pCount, aCount, i, status, lPids[NUMPIPES];
    pid_t pid;

    pipe(fdPipe);

    while(1) {
        bBuffer = readline("Shell> ");

        if(!strcasecmp(bBuffer, "exit")) {
            return 0;
        }

        sPtr = bBuffer;
        pCount = -1;

        do {
            aPtr = strsep(&sPtr, "|");
            pipeComms[++pCount] = aPtr;
        } while(aPtr);

        for(i = 0; i < pCount; i++) {
            aCount = -1;

            do {
                aPtr = strsep(&pipeComms[i], " ");
                cmdArgs[++aCount] = aPtr;
            } while(aPtr);

            cmdArgs[aCount] = 0;

            if(strlen(cmdArgs[0]) > 0) {
                pid = fork();

                if(pid == 0) {
                    if(i == 0) {
                        close(fdPipe[0]);

                        dup2(fdPipe[1], STDOUT_FILENO);

                        close(fdPipe[1]);
                    } else if(i == 1) {
                        close(fdPipe[1]);

                        dup2(fdPipe[0], STDIN_FILENO);

                        close(fdPipe[0]);
                    }

                    execvp(cmdArgs[0], cmdArgs);
                    exit(1);
                } else {
                    lPids[i] = pid;

                    /*waitpid(pid, &status, 0);

                    if(WIFEXITED(status)) {
                        printf("[%d] TERMINATED (Status: %d)\n",
                            pid, WEXITSTATUS(status));
                    }*/
                }
            }
        }

        for(i = 0; i < pCount; i++) {
            waitpid(lPids[i], &status, 0);

            if(WIFEXITED(status)) {
                printf("[%d] TERMINATED (Status: %d)\n",
                    lPids[i], WEXITSTATUS(status));
            }
        }
    }

    return 0;
}

(代码已更新,以反映以下两个答案提出的更改,但仍无法正常工作…)
以下是失败的测试用例:
nazgulled ~/Projects/SO/G08 $ ls -l
total 8
-rwxr-xr-x 1 nazgulled nazgulled  7181 2009-05-27 17:44 a.out
-rwxr-xr-x 1 nazgulled nazgulled   754 2009-05-27 01:42 data.h
-rwxr-xr-x 1 nazgulled nazgulled  1305 2009-05-27 17:50 main.c
-rwxr-xr-x 1 nazgulled nazgulled   320 2009-05-27 01:42 makefile
-rwxr-xr-x 1 nazgulled nazgulled 14408 2009-05-27 17:21 prog
-rwxr-xr-x 1 nazgulled nazgulled  9276 2009-05-27 17:21 prog.c
-rwxr-xr-x 1 nazgulled nazgulled 10496 2009-05-27 17:21 prog.o
-rwxr-xr-x 1 nazgulled nazgulled    16 2009-05-27 17:19 test
nazgulled ~/Projects/SO/G08 $ ./a.out
Shell> ls -l|grep prog
[4804] TERMINATED (Status: 0)
-rwxr-xr-x 1 nazgulled nazgulled 14408 2009-05-27 17:21 prog
-rwxr-xr-x 1 nazgulled nazgulled  9276 2009-05-27 17:21 prog.c
-rwxr-xr-x 1 nazgulled nazgulled 10496 2009-05-27 17:21 prog.o

问题是,我应该回到我的shell之后,我应该看到“shell>”等待更多的输入。您还可以注意到,您没有看到类似于“[4804]terminated(status:0)”的消息(但具有不同的PID),这意味着第二个进程没有终止。
我认为这与grep有关,因为它起作用:
nazgulled ~/Projects/SO/G08 $ ./a.out
Shell> echo q|sudo fdisk /dev/sda
[4838] TERMINATED (Status: 0)

The number of cylinders for this disk is set to 1305.
There is nothing wrong with that, but this is larger than 1024,
and could in certain setups cause problems with:
1) software that runs at boot time (e.g., old versions of LILO)
2) booting and partitioning software from other OSs
   (e.g., DOS FDISK, OS/2 FDISK)

Command (m for help):
[4839] TERMINATED (Status: 0)

你可以很容易地看到两条“终止”消息…
那么,我的代码怎么了?

最佳答案

即使在您的管道退出的第一个命令(和TSUTHORT stdout=~fdPipe[1])之后,父级仍然具有fdPipe[1]打开。
因此,管道的第二个命令有一个永远不会得到eof的stdin=~fdPipe[0],因为管道的另一个端点仍然打开。
您需要为每个pipe(fdPipe)创建一个新的|,并确保关闭父节点中的两个端点;即。

for cmd in cmds
    if there is a next cmd
        pipe(new_fds)
    fork
    if child
        if there is a previous cmd
            dup2(old_fds[0], 0)
            close(old_fds[0])
            close(old_fds[1])
        if there is a next cmd
            close(new_fds[0])
            dup2(new_fds[1], 1)
            close(new_fds[1])
        exec cmd || die
    else
        if there is a previous cmd
            close(old_fds[0])
            close(old_fds[1])
        if there is a next cmd
            old_fds = new_fds
if there are multiple cmds
    close(old_fds[0])
    close(old_fds[1])

此外,为了更安全,您应该在执行任何fdPipe{STDIN_FILENO,STDOUT_FILENO}操作之前处理closedup2重叠的情况。如果有人成功地在stdin或stdout关闭的情况下启动shell,则可能会发生这种情况,并将导致与此处的代码严重混淆。
编辑
   fdPipe1           fdPipe3
      v                 v
cmd1  |  cmd2  |  cmd3  |  cmd4  |  cmd5
               ^                 ^
            fdPipe2           fdPipe4

除了确保关闭父管道中的管道端点外,我还试图使fdPipe1fdPipe2等点不能相同。
/* suppose stdin and stdout have been closed...
 * for example, if your program was started with "./a.out <&- >&-" */
close(0), close(1);

/* then the result you get back from pipe() is {0, 1} or {1, 0}, since
 * fd numbers are always allocated from the lowest available */
pipe(fdPipe);

close(0);
dup2(fdPipe[0], 0);

我知道你现在的代码中没有使用pipe(),但最后一段警告你要小心这个案例。
编辑
对代码的以下最小更改使其在您提到的特定失败情况下工作:
@@-12,6+12,4个@@
PID_t PID;
-管道(FDPIPE);
-
当(1){
bbuffer=readline(“shell>”);
@@-29,4+27,6个@@
}while(aptr);
+管道(FDPIPE);
+
对于(i=0;iA计数=-1;
@@-72,4+72,7号@@
}
+关闭(FDPIPE[0]);
+关闭(FDPIPE[1]);
+
对于(i=0;iwaitpid(lpid[i],&status,0);
这对于管道中的多个命令不起作用;为此,您需要这样的东西:(未经测试,因为您还必须修复其他问题)
@@-9,9+9,7号@@
int main(int argc,char*argv[]){
char*bbuffer,*sptr,*aptr=null,*pipecomms[numpipes],*cmdargs[10];
-int fdpipe[2],pcount,acount,i,status,lpid[numpipes];
+int fdpipe[2]、fdpipe2[2]、pcount、acount、i、status、lpid[numpipes];
PID_t PID;
-管道(FDPIPE);
-
当(1){
bbuffer=readline(“shell>”);
@@-32,4+30,7个@@
A计数=-1;
+如果(i+1+管道(FDPIpe2);
+
做{
aptr=strsep(&pipecomms[i],“”);
@@-43,11+44,12个@@
如果(PID==0){
-如果(i==0){
-关闭(FDPIPE[0]);
+如果(i+1+关闭(fdpipe2[0]);
-dup2(fdpipe[1],stdout_fileno);
+dup2(fdpipe2[1],标准输出文件号);
-关闭(FDPIPE[1]);
-}否则if(i==1){
+关闭(fdpipe2[1]);
+}
+如果(我!=0){
关闭(FDPIPE[1]);
@@-70,4+72,17号@@
}
}
+
+如果(我!=0){
+关闭(FDPIPE[0]);
+关闭(FDPIPE[1]);
+}
+
+FDPipe[0]=FDPipe2[0];
+FDPipe[1]=FDPipe2[1];
+}
+
+如果(计数){
+关闭(FDPIPE[0]);
+关闭(FDPIPE[1]);
}

08-16 23:08