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

时间:2012-03-23 04:13:02

标签: bash pipe

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

dmesg | less

虽然我知道这会输出什么,但需要dmesg并让我用less滚动浏览它,我不明白|正在做什么。它只是>的反面吗?

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

11 个答案:

答案 0 :(得分:63)

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

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

你的第三个问题有点含糊不清。是的,管道本身在bash脚本中是一致的。但是,管道符|可以表示不同的内容。双管(||)代表“或”运算符,例如。

答案 1 :(得分:18)

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

  1. fd#0表示流程的标准输入
  2. fd#1表示流程的标准输出
  3. fd#2表示进程的标准错误输出
  4. 通常,当您运行简单程序时,默认情况下这些文件描述符配置如下:

    1. 从键盘读取默认输入
    2. 标准输出配置为监视器
    3. 标准错误也被配置为监视器
    4. Bash提供了几个运算符来更改此行为(例如,请查看>,>>和<运算符)。因此,您可以将输出重定向到标准输出以外的其他输出,或者从键盘以外的其他流中读取输入。当两个程序以协作以一种方式使用另一个程序的输出作为其输入的情况时,特别有趣。为了简化这种协作,Bash提供了管道运算符|。请注意协作的使用,而不是链接。我避免使用这个术语,因为实际上管道不是连续的。带管道的普通命令行具有以下方面:

          > program_1 | program_2 | ... | program_n
      

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

      以下是Creating pipe in 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);
      }
      

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

      > program_1 | program_2 | program_3
      

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

答案 2 :(得分:14)

Unix中的每个标准进程至少有三个文件描述符,有点像 interfaces

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

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

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

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

答案 3 :(得分:6)

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

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

echo "Hello world" > helloworld.txt

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

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

  

ls -l | grep helloworld.txt

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

grep等程序在没有任何参数的情况下使用时,只需阅读并等待在标准输入(stdin)上传递的内容。当他们捕捉到某些内容时,例如ls命令的输出,grep通常会通过查找您正在搜索的内容来发挥作用。

答案 4 :(得分:2)

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

答案 5 :(得分:2)

  • |将左侧命令的STDOUT置于右侧命令的STDIN。

  • 如果您使用多个管道,它只是一个管道链。第一个命令输出设置为第二个命令输入。第二个命令输出设置为下一个命令输入。等等。

  • 它可以在所有基于Linux / widows的命令解释器中使用。

答案 6 :(得分:2)

管道非常简单。

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

例如: ls | grep my | grep文件

首先列出工作目录中的文件。 grep命令检查此输出是否为“my”。现在输出到第二个grep命令,最后搜索单词“files”。而已。

答案 7 :(得分:1)

如果将每个unix命令视为独立模块,则 但是你需要他们使用文本作为一致的界面互相交谈,
怎么做到呢?

cmd                       input                    output

echo "foobar"             string                   "foobar" 
cat "somefile.txt"        file                     *string inside the file*
grep "pattern" "a.txt"    pattern, input file      *matched string*

你可以说|是在接力马拉松中传递接力棒的比喻 它甚至形状像一个! cat -> echo -> less -> awk -> perl类似于cat | echo | less | awk | perl

cat "somefile.txt" | echo
cat将其echo的输出传递给使用。

当有多个输入时会发生什么? cat "somefile.txt" | grep "pattern"
grep有一个隐含的规则,即“将其作为输入文件而不是模式”传递。
你会慢慢发展,以了解哪个参数是经验。

答案 8 :(得分:1)

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

stdin, stdout, stderr

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

simple pipeline

它的工作方式有很多细微之处,例如,stderr流可能没有按您期望的那样进行管道传输,如下所示:

stderr and redirection

我花了很多时间尝试在Bash中编写详细但对初学者友好的管道说明。完整内容位于:

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

答案 9 :(得分:1)

关于管道的效率问题:

  • 一个命令可以在前一个管道命令完成之前访问和处理其输入处的数据,这意味着如果资源可用,则计算能力利用效率。
  • Pipe 不需要在下一个命令访问其输入之前将命令的输出保存到文件中(两个命令之间没有 I/O 操作),这意味着减少了昂贵的 I/O 操作和磁盘空间效率。< /li>

答案 10 :(得分:0)

所有这些答案都很棒。我想提一提的是,bash中的管道(与unix / linux或Windows命名为pipe的概念相同)就像现实生活中的管道一样。 如果您将管道之前的程序视为水源,将管道视为水管,而将管道之后的程序视为用水的东西(程序输出为水),那么您几乎可以理解管道是如何工作的。工作。 请记住,管道中的所有应用程序都是并行运行的。