2016-11-23 259 views
2

我試圖寫一個程序,將執行命令:命名管道,chaning標準輸入,在標準輸出叉()

ls -al | tr 'a-z' 'A-Z' | grep X > testing 

基本上我想學習如何在C.將管道有兩次是很多帖子,我們只使用一個'|'。我根據只有一個'|'的例子做了一些嘗試但可悲的是它不起作用。有一個代碼。

#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <fcntl.h> 
#define MAX 512 
int main(int argc,char** argv){ 
    int pdesk[2]; 
    int pipedesk[2]; 
    if(pipe(pipedesk)==-1 || pipe(pdesk)==-1){ 
     perror("Pipe"); 
     exit(1); 
    } 
    switch(fork()){ 
     case -1 : 
       perror("Creating process"); 
       exit(1); 
     case 0: 
       dup2(pdesk[1],STDOUT_FILENO); 
       close(pdesk[1]); 
       execlp("ls","ls","-al",NULL); 
       perror("ls"); 
       exit(1); 
     default: { 
       if(fork()==0){ 
        if(fork()==0){ 
         dup2(pdesk[0],STDIN_FILENO); 
         close(pdesk[0]); 
         dup2(pipedesk[1],STDOUT_FILENO); 
         close(pipedesk[1]); 
         execlp("tr","tr","a-z","A-Z",NULL); 
         perror("tr"); 
         exit(1); 
        } 
        dup2(pipedesk[0],STDIN_FILENO); 
        close(pipedesk[0]); 
        int desk=open("testing",O_WRONLY|O_CREAT,0640); 
        if(desk==-1){ 
         perror("opening file"); 
         exit(1); 
        } 
        dup2(desk,STDOUT_FILENO); 
        if(close(desk)==-1){ 
         perror("closing file"); 
         exit(1); 
        } 
        execlp("grep","grep","X",NULL); 
        perror("grep"); 
        exit(1); 
       } 
//    wait(NULL); 

     } 
     return 0; 
    } 
} 
+1

不起作用??? –

+0

請避免使用[*幻數*](https://en.wikipedia.org/wiki/Magic_number_(編程))。改爲使用預定義的宏,例如'STDIN_FILENO'和'STDOUT_FILENO'。此外,你錯過了一個關鍵的頭文件包含。 –

+0

至於你的問題,你需要*兩個*管道。您不能重複使用現有的管道。 –

回答

0

我不認爲信號處理是相關的。有關的是確保管道正確關閉。如果有一個寫入結束的進程打開,進程將不會在管道的讀取端獲得EOF。這包括當前的過程;如果一個進程同時打開了管道的讀寫端,它將永遠不會在管道的讀端讀取EOF,因爲理論上有一個進程可以寫入該進程。這樣的過程因此永久地掛在管道上的read()中。

這個版本的代碼爲我工作得很好:

#include <fcntl.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <sys/wait.h> 
#include <unistd.h> 

int main(void) 
{ 
    int pdesk[2]; 
    int pipedesk[2]; 
    int pid; 

    if (pipe(pipedesk) == -1 || pipe(pdesk) == -1) 
    { 
     perror("Pipe"); 
     exit(1); 
    } 
    //fprintf(stderr, "PID %d: controller\n", (int)getpid()); 

    switch (pid = fork()) 
    { 
    case -1: 
     perror("Creating process"); 
     exit(1); 
    case 0: 
     //fprintf(stderr, "PID %d: will be 'ls'\n", (int)getpid()); 
     dup2(pdesk[1], STDOUT_FILENO); 
     close(pdesk[0]);  // JL 
     close(pdesk[1]); 
     close(pipedesk[0]);  // JL 
     close(pipedesk[1]);  // JL 
     execlp("ls", "ls", "-al", (char *)NULL); 
     perror("ls"); 
     exit(1); 
    default: 
     //fprintf(stderr, "PID %d: ls process PID = %d\n", (int)getpid(), pid); 
     if ((pid = fork()) == 0) 
     { 
      //fprintf(stderr, "PID %d: about to fork 'tr'\n", (int)getpid()); 
      if ((pid = fork()) == 0) 
      { 
       //fprintf(stderr, "PID %d: will be 'tr'\n", (int)getpid()); 
       dup2(pdesk[0], STDIN_FILENO); 
       close(pdesk[0]); 
       close(pdesk[1]);  // JL 
       dup2(pipedesk[1], STDOUT_FILENO); 
       close(pipedesk[0]);  // JL 
       close(pipedesk[1]); 
       execlp("tr", "tr", "a-z", "A-Z", (char *)NULL); 
       perror("tr"); 
       exit(1); 
      } 
      //fprintf(stderr, "PID %d: about to exec 'grep'\n", (int)getpid()); 
      dup2(pipedesk[0], STDIN_FILENO); 
      close(pdesk[0]);  // JL 
      close(pdesk[1]);  // JL 
      close(pipedesk[0]); 
      close(pipedesk[1]);  // JL 
      int desk = open("testing", O_WRONLY | O_CREAT, 0644); 
      if (desk == -1) 
      { 
       perror("opening file"); 
       exit(1); 
      } 
      dup2(desk, STDOUT_FILENO); 
      if (close(desk) == -1) 
      { 
       perror("closing file"); 
       exit(1); 
      } 
      execlp("grep", "grep", "X", (char *)NULL); 
      perror("grep"); 
      exit(1); 
     } 

     //fprintf(stderr, "PID %d: closing pipes\n", (int)getpid()); 
     close(pdesk[0]);  // JL 
     close(pdesk[1]);  // JL 
     close(pipedesk[0]);  // JL 
     close(pipedesk[1]);  // JL 

     break; 
    } 

    int status; 
    int corpse; 
    while ((corpse = wait(&status)) != -1) 
     fprintf(stderr, "PID %d: child %d died 0x%.4X\n", (int)getpid(), corpse, status); 

    return 0; 
} 

注意所有的行標// JL這是關閉操作。雖然你可以在沒有少數人的情況下離開(例如,對於第一個孩子來說可能是'可選的'),但你應該按照慣例關閉所有你不使用的管道。請注意,特別是關於原始父進程(最後四個)的關閉 - 這些都很重要。

未關閉足夠多的管道描述符是使用多個管道時最常見的錯誤之一。通常,如果使用dup2()(或dup())將管道描述符複製到標準輸入或輸出,則應關閉兩個兩端的原始管道。如果你的進程根本沒有使用管道描述符,你應該關閉它們。從程序

輸出樣本:

$ ./pp61 
PID 35665: child 35666 died 0x0000 
PID 35665: child 35667 died 0x0000 
$ 

testing文件的內容示例:

DRWXR-XR-X 44 JLEFFLER STAFF 1496 NOV 23 17:28 . 
DRWXR-XR-X 137 JLEFFLER STAFF 4658 NOV 23 17:28 .. 
DRWXR-XR-X 18 JLEFFLER STAFF 612 OCT 23 20:00 .GIT 
DR-XR-XR-X 4 JLEFFLER STAFF 136 AUG 14 23:27 SAFE 
DRWXR-XR-X 35 JLEFFLER STAFF 1190 NOV 23 09:19 UNTRACKED 
-RW-R--R-- 1 JLEFFLER STAFF  81 NOV 20 20:53 ANIMALS.TXT 
DRWXR-XR-X 3 JLEFFLER STAFF 102 SEP 12 00:03 DOC 
DRWXR-XR-X 8 JLEFFLER STAFF 272 NOV 10 20:58 ETC 
DRWXR-XR-X 17 JLEFFLER STAFF 578 JUL 15 19:06 INC 
-RW-R--R-- 1 JLEFFLER STAFF 1217 NOV 21 21:26 IX37.SQL 
DRWXR-XR-X 5 JLEFFLER STAFF 170 JUL 9 23:47 LIB 
-RWXR-XR-X 1 JLEFFLER STAFF 9052 NOV 19 13:01 LL73 
DRWXR-XR-X 3 JLEFFLER STAFF 102 NOV 6 15:40 LL73.DSYM 
-RWXR-XR-X 1 JLEFFLER STAFF 8896 NOV 6 10:38 LL83 
DRWXR-XR-X 3 JLEFFLER STAFF 102 NOV 6 10:10 LL83.DSYM 
-RW-R--R-- 1 JLEFFLER STAFF 108 NOV 20 20:53 NEWANIMAL.TXT 
-RWXR-XR-X 1 JLEFFLER STAFF 9124 NOV 20 20:38 PD43 
DRWXR-XR-X 3 JLEFFLER STAFF 102 NOV 20 20:38 PD43.DSYM 
-RWXR-XR-X 1 JLEFFLER STAFF 9148 NOV 23 17:28 PP61 
DRWXR-XR-X 3 JLEFFLER STAFF 102 NOV 23 14:11 PP61.DSYM 
-RWXR-XR-X 1 JLEFFLER STAFF 9016 NOV 23 11:07 RS19 
DRWXR-XR-X 3 JLEFFLER STAFF 102 NOV 23 10:03 RS19.DSYM 
DRWXR-XR-X 146 JLEFFLER STAFF 4964 OCT 9 17:06 SRC 
-RWXR-XR-X 1 JLEFFLER STAFF 8760 NOV 23 13:12 STP83 
DRWXR-XR-X 3 JLEFFLER STAFF 102 NOV 23 13:04 STP83.DSYM 
DRWXR-XR-X 6 JLEFFLER STAFF 204 NOV 6 21:52 TMP 
-RWXR-XR-X 1 JLEFFLER STAFF 8808 NOV 23 10:38 TR37 
DRWXR-XR-X 3 JLEFFLER STAFF 102 NOV 23 10:38 TR37.DSYM 
-RWXR-XR-X 1 JLEFFLER STAFF 26772 NOV 21 20:18 XY73 
DRWXR-XR-X 3 JLEFFLER STAFF 102 NOV 21 20:18 XY73.DSYM 
-RW-R--R-- 1 JLEFFLER STAFF 467 NOV 21 20:18 XY73.L 
2

當管道的另一端死亡時,您的進程將收到一個SIGPIPE,該進程默認會終止進程。

所以你至少應該忽略它!

signal(SIGPIPE, SIG_IGN); 

爲了保持乾淨,您應該關閉所有不會在當前過程中使用的管端。

例如:

case 0: 
    close(pdesk[0]); 
    close(pipedesk[0]); 
    close(pipedesk[1]); 
+0

謝謝)回答。所以在添加信號(SIGPIPE,SIG_IGN)之後,我的程序將嵌入SIGPIPE信號,對嗎?我添加了,在main()的開頭添加了這個命令,但是遺憾的是沒有任何變化......你是否介意給我一些更多的提示讓我的程序工作? – ogarogar

0

這裏是一個很容易理解的實現。

當每個過程都關閉了管道的另一端時,管道的一端就會關閉。所以這對於關閉當前流程無用的每個管道非常重要。

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

void closeAll(const int * fd_to_close){ 
    int i = 0; 
    while(1){ 
     int fd = fd_to_close[i]; 
     if(fd == -1){ 
      break; 
     } 
     close(fd); 
     i++; 
    } 
} 

pid_t forkExec(int my_stdin, int my_stdout, int my_stderr, char *const argv[], const int* fd_to_close){ 
    pid_t child = fork(); 

    if(child != 0){ 
     return child; 
    } 

    if(my_stdin != STDIN_FILENO){ 
     dup2(my_stdin, STDIN_FILENO); 
    } 
    if(my_stdout != STDOUT_FILENO){ 
     dup2(my_stdout, STDOUT_FILENO); 
    } 
    if(my_stderr != STDERR_FILENO){ 
     dup2(my_stderr, STDERR_FILENO); 
    } 

    closeAll(fd_to_close); 

    execvp(argv[0], argv); 

    perror("Executing "); 

    exit(1); 
} 

void waitFor(pid_t child){ 
    int status; 
    pid_t w = waitpid(child, &status, 0); 
    if(w == -1){ 
     perror("child "); 
    } else { 
     printf("Exit status of child %d was %d, killed by signal %d %s\n", (int) child, WEXITSTATUS(status), WTERMSIG(status), WCOREDUMP(status) ? "with coredump" : ""); 
    } 
} 

void main(int argc, char**argv){ 
    int pipe1[2]; 
    int pipe2[2]; 
    int fd_to_close[10]; 

    signal(SIGPIPE, SIG_IGN); 

    if(pipe(pipe1)==-1 || pipe(pipe2)==-1){ 
     perror("Pipe"); 
     exit(1); 
    } 

    fd_to_close[0] = pipe1[0]; 
    fd_to_close[1] = pipe1[1]; 
    fd_to_close[2] = pipe2[0]; 
    fd_to_close[3] = pipe2[1]; 
    fd_to_close[4] = -1; 

    char* cmd1[] = {"ls", "-al", NULL}; 
    char* cmd2[] = {"tr", "a-z", "A-Z", NULL}; 
    char* cmd3[] = {"grep", "X", NULL}; 

    pid_t child1 = forkExec(STDIN_FILENO, pipe1[1],  STDERR_FILENO, cmd1, fd_to_close); 
    pid_t child2 = forkExec(pipe1[0],  pipe2[1],  STDERR_FILENO, cmd2, fd_to_close); 
    pid_t child3 = forkExec(pipe2[0],  STDOUT_FILENO, STDERR_FILENO, cmd3, fd_to_close); 

    // Very important : 
    closeAll(fd_to_close); 

    printf("pid of %s is %d\n", cmd1[0], (int)child1); 
    printf("pid of %s is %d\n", cmd2[0], (int)child2); 
    printf("pid of %s is %d\n", cmd3[0], (int)child3); 

    waitFor(child1); 
    waitFor(child2); 
    waitFor(child3); 
}