2016-11-23 181 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); 
}