管道如何在Bash中工作的简单解释是什么?

Vil*_*age 59 bash pipe

我经常在Bash中使用管道,例如:

dmesg | less
Run Code Online (Sandbox Code Playgroud)

虽然我知道这是什么输出,它需要dmesg并让我滚动它less,我不明白|它在做什么.它恰恰相反>吗?

  • 对于什么有简单或隐喻的解释|
  • 在一条线路中使用多个管道时会发生什么?
  • 管道的行为在Bash脚本中出现的位置是否一致?

qua*_*cle 72

Unix管道将第一个进程的STDOUT(标准输出)文件描述符连接到第二个进程的STDIN(标准输入).然后会发生的是,当第一个进程写入其STDOUT时,第二个进程可以立即读取该输出(来自STDIN).

使用多个管道与使用单个管道没有什么不同.每个管道都是独立的,只需链接相邻进程的STDOUT和STDIN.

你的第三个问题有点含糊不清.是的,管道本身在bash脚本中是一致的.但是,管道角色|可以代表不同的东西.例如,Double pipe(||)表示"或"运算符.

  • 注意单词"__immediately__"!我指出这一点是因为我们使用Bash进行随意脚本编写往往将我们的命令视为同步,我们的脚本完全是顺序的.我们希望管道执行left命令,并将其输出传递给以下命令.但是管道使用分叉,而命令实际上是并行执行的.对于许多命令来说,这个事实在功能上是无关紧要的,但[有时很重要](http://stackoverflow.com/q/6893714/445295).例如,检查输出:`ps | cat`. (33认同)

rka*_*ach 23

在Linux(和一般的Unix)中,每个进程都有三个默认文件描述符:

  1. fd#0表示进程的标准输入
  2. fd#1表示进程的标准输出
  3. fd#2表示进程的标准错误输出

通常,当您运行简单程序时,默认情况下这些文件描述符配置如下:

  1. 从键盘读取默认输入
  2. 标准输出配置为监视器
  3. 标准错误也配置为监视器

Bash提供了几个操作符来改变这种行为(例如,查看>,>>和<运算符).因此,您可以将输出重定向到标准输出以外的其他输出,或者从键盘以外的其他流中读取输入.特别有趣的是,当两个程序以这样的方式进行协作时,使用另一个程序的输出作为其输入.为了简化这种协作,Bash提供了管道操作员|.请注意协作的使用而不是链接.我避免使用这个术语,因为实际上管道不是顺序的.带管道的普通命令行具有以下方面:

    > program_1 | program_2 | ... | program_n
Run Code Online (Sandbox Code Playgroud)

上面的命令行有点误导:一旦program_1完成执行,用户可能会认为program_2获得了输入,这是不正确的.事实上,bash所做的是并行启动所有程序,并相应地配置输入输出,以便每个程序从前一个程序获取输入并将其输出传递给下一个程序(在命令行建立顺序中).

以下是在C中创建管道,在父进程和子进程之间创建管道的简单示例.重要的部分是对pipe()的调用以及父如何关闭fd 1(写边)以及子如何关闭fd 1(写边).请注意,管道是单向通信通道.因此,数据只能在一个方向上流动:fd 1朝向fd [0].有关更多信息,请查看pipe()的手册页.

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

int main(void)
{
    int     fd[2], nbytes;
    pid_t   childpid;
    char    string[] = "Hello, world!\n";
    char    readbuffer[80];

    pipe(fd);

    if((childpid = fork()) == -1)
    {
            perror("fork");
            exit(1);
    }

    if(childpid == 0)
    {
            /* Child process closes up input side of pipe */
            close(fd[0]);

            /* Send "string" through the output side of pipe */
            write(fd[1], string, (strlen(string)+1));
            exit(0);
    }
    else
    {
            /* Parent process closes up output side of pipe */
            close(fd[1]);

            /* Read in a string from the pipe */
            nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
            printf("Received string: %s", readbuffer);
    }

    return(0);
}
Run Code Online (Sandbox Code Playgroud)

最后但并非最不重要的,当您在表单中有一个命令行时:

> program_1 | program_2 | program_3
Run Code Online (Sandbox Code Playgroud)

整行的返回码设置为最后一个命令.在这种情况下program_3.如果您想获得中间返回码,您必须设置pipefail或从PIPESTATUS获取.


Edu*_*nec 15

Unix中的每个标准进程至少有三个文件描述符,它们有点像接口:

  • 标准输出,即进程打印其数据的位置(大多数情况下是控制台,即屏幕或终端).
  • 标准输入,它是从中获取数据的地方(大多数情况下它可能类似于键盘).
  • 标准错误,这是错误和有时其他带外数据发生的地方.它现在没有意思,因为管道通常不处理它.

管道将过程的标准输出连接到左侧的过程的标准输入.您可以将其视为一个专用程序,负责复制一个程序打印的所有内容,并将其提供给下一个程序(管道符号后面的程序).这不是那个,但它足够类比.

每个管道只运行两件事:标准输出来自其左侧,输入流预期位于其右侧.其中每个都可以附加到单个进程或管道的另一个位,这是多管道命令行中的情况.但这与管道的实际操作无关; 每个管道都有自己的.

重定向operator(>)执行相关操作,但更简单:默认情况下,它将进程的标准输出直接发送到文件.正如你所看到的那样,它不是管道的反面,而是实际上是互补的.>不足为奇的是<,它获取文件的内容并将其发送到进程的标准输入(将其视为一个逐字节读取文件并在进程中为您键入的程序).


Hal*_*oum 7

管道接受进程的输出,输出我的意思是标准输出(stdout在UNIX上)并将其传递给(stdin)另一个进程的标准输入.它与简单的右重定向不同>,目的是将输出重定向到另一个输出.

例如,在Linux上使用echo命令,它只是在标准输出上打印参数传递的字符串.如果您使用简单的重定向,例如:

echo "Hello world" > helloworld.txt
Run Code Online (Sandbox Code Playgroud)

shell将重定向最初打算在stdout上的正常输出,并将其直接打印到文件中helloworld.txt.

现在,举个涉及管道的例子:

ls -l | grep helloworld.txt

ls命令的标准输出将在grep的输入处输出,那么这是如何工作的?

诸如grep在没有任何参数的情况下使用它们之类的程序只是简单地阅读并等待在标准输入上传递的东西(stdin).当他们捕获某些内容时,例如ls命令的输出,grep通常会通过查找您正在搜索的内容来发挥作用.


Dav*_*err 7

简而言之,如上所述,需要注意三个关键的“特殊”文件描述符。默认情况下,外壳将键盘stdin发送到屏幕并发送stdout和发送stderr到屏幕:

标准输入、标准输出、标准错误

管道只是一种 shell 便利,它将stdout一个进程的 直接附加到下一个进程stdin的 :

简单的管道

它的工作方式有很多微妙之处,例如,stderr流可能不会像您期望的那样通过管道传输,如下所示:

标准错误和重定向

我花了相当多的时间试图写一篇关于 Bash 管道的详细但对初学者友好的解释。完整内容在:

https://effective-shell.com/docs/part-2-core-skills/7-thinking-in-pipelines/

  • 图会说话! (3认同)

Xan*_*der 5

管道就像这样非常简单。

你有一个命令的输出。您可以使用管道将此输出作为输入提供给另一个命令。您可以根据需要通过管道传输任意数量的命令。

例如: ls | 我的 | grep文件

这首先列出了工作目录中的文件。此输出由 grep 命令检查单词“my”。这个输出现在进入第二个 grep 命令,它最终搜索单词“files”。就是这样。


fra*_*nka 4

管道运算符获取第一个命令的输出,并通过连接标准输入和标准输出将其“管道”到第二个命令。在您的示例中,dmesg 命令的输出不是转到 stdout(并将其扔到控制台上),而是直接进入您的下一个命令。

  • 管道不将输出作为参数传递。管道将 STDOUT 连接到 STDIN。某些命令必须经过专门指示才能查看 STDIN(通常通过给出连字符而不是文件名)才能在管道中使用。 (2认同)