【问题标题】:How to handle logic chain of filters of shell commands?如何处理shell命令过滤器的逻辑链?
【发布时间】:2019-07-09 18:30:00
【问题描述】:

我正在阅读一本操作系统书籍,并发现这个示例可以工作。我明白ps -elf | less 在做什么。 |(管道)充当ps -elfless 命令之间的桥梁,并将ps -elf 的输出作为less 的输入。

但在 shell 命令中更深入我试图了解会发生什么

ps -elf | grep "/usr" | wc –l 做吗?

在这种情况下,有两个|(管道),逻辑相同。但我无法将它实施到一个工作示例中。如果你知道怎么做,那对我来说就很清楚了。

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
    int fds[2];
    char buf[30];
    pid_t pid1, pid2, pid;
    int status, i;

    /* create a pipe */
    if (pipe(fds) == -1) {
            perror("pipe");
            exit(1);
    }

    /* fork first child */
    if ( (pid1 = fork()) < 0) {
    perror("fork");
    exit(1);
    }

    if ( pid1 == 0 ) {
        close(1);  /* close normal stdout (fd = 1) */
        dup2(fds[1], 1);   /* make stdout same as fds[1] */
        close(fds[0]); /* we don't need the read end -- fds[0] */

        if( execlp("ps", "ps", "-elf", (char *) 0) < 0) {
        perror("Child");
        exit(0);
    }

    /* control never reaches here */
    } 

    /* fork second child */
    if ( (pid2 = fork()) < 0) {
    perror("fork");
    exit(1);
    }

    if ( pid2 == 0 ) {
        close(0);   /* close normal stdin (fd = 0)*/
        dup2(fds[0],0);   /* make stdin same as fds[0] */
        close(fds[1]); /* we don't need the write end -- fds[1]*/

        if( execlp("less", "less", (char *) 0) < 0) {
        perror("Child");
        exit(0);
    }

    /* control never reaches here */
    }

    /* parent doesn't need fds  - MUST close - WHY? */
    close(fds[0]); 
    close(fds[1]); 

    /* parent waits for children to complete */
    for( i=0; i<2; i++) {
        pid = wait(&status);
        printf("Parent: Child %d completed with status %d\n", pid, status);
    }
}

【问题讨论】:

    标签: c shell operating-system


    【解决方案1】:

    创建管道时,您需要从管道中的 last 命令开始,然后从该命令向后工作。通过这样做,您启动的每个程序都在等待管道中先前命令的输出。如果从前面开始,第一个程序可能会在您有机会启动第二个程序之前完成,从而导致管道被关闭。

    在这种情况下,您首先运行less。那就是等待尚未到来的输出。然后你运行ps,它的输出被馈送到less

    /* fork first child */
    if ( (pid1 = fork()) < 0) {
        perror("fork");
        exit(1);
    }
    
    if ( pid1 == 0 ) {
        close(0);   /* close normal stdin (fd = 0)*/
        dup2(fds[0],0);   /* make stdin same as fds[0] */
        close(fds[1]); /* we don't need the write end -- fds[1]*/
    
        if( execlp("less", "less", (char *) 0) < 0) {
            perror("Child");
            exit(0);
        }
    
        /* control never reaches here */
    } 
    
    /* fork second child */
    if ( (pid2 = fork()) < 0) {
        perror("fork");
        exit(1);
    }
    
    if ( pid2 == 0 ) {
        close(1);  /* close normal stdout (fd = 1) */
        dup2(fds[1], 1);   /* make stdout same as fds[1] */
        close(fds[0]); /* we don't need the read end -- fds[0] */
    
        if( execlp("ps", "ps", "-elf", (char *) 0) < 0) {
            perror("Child");
            exit(0);
        }
    
        /* control never reaches here */
    }
    

    【讨论】:

    • 进程创建的时间顺序与管道连接的拓扑结构有什么关系?
    • @EricPostpischil 如果从前面启动管道,则一个应用程序可以在下一个应用程序启动之前完成并关闭管道。从头开始可以防止这些类型的时间问题,我相信大多数 shell 也是这样做的。
    • 谢谢。将其包含在答案中可能很有用。我想知道这是否重要。如果第一个进程完成,然后你创建第二个进程,第二个进程应该仍然继承你的管道上下文,所以它应该仍然能够读取管道缓冲区中的任何数据,然后获得关闭/EOF 指示,不应该吗?
    • 好的。说得通。我理解错了。但主要问题是这个ps -elf | grep "/usr" | wc –l。这个命令是如何工作的?如何实现它以更好地了解这些管道的工作原理。
    • @RajatGupta 在这种情况下,您需要创建两个管道。第一个在grepwc 之间,第二个在psgrep 之间。从第一个管道开始 wc 读取,然后开始 grep 写入第一个管道并从第二个管道读取,然后开始 ps 写入第二个管道。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-02-09
    • 1970-01-01
    • 1970-01-01
    • 2016-06-14
    • 1970-01-01
    • 2014-02-23
    • 1970-01-01
    相关资源
    最近更新 更多