【问题标题】:Pipe | Redirection < > Precedence管道 |重定向<>优先级
【发布时间】:2012-10-08 04:00:22
【问题描述】:

我想明确何时使用管道 |还是重定向 在命令中优先?

这是我的想法,但需要确认它是如何工作的。

示例 1:

sort < names | head
The pipe runs first:  names|head   then it sorts what is returned from names|head

示例 2:

ls | sort > out.txt
This one seems straight forward by testing, ls|sort then redirects to out.txt

示例 3:

Fill in the blank?  Can you have both a < and a > with a | ???

【问题讨论】:

标签: bash unix redirect command pipe


【解决方案1】:

更正:

示例 1:

sort < names | head

在这种情况下,输入重定向首先运行(名称已排序),然后将结果通过管道传送到 head。

一般来说,您可以从左到右阅读。标准成语的作用如下:

  • 使用输入重定向“
  • 使用输出重定向“>”告诉程序输出到文件而不是标准输出
  • 使用管道“program_a | program_b”会将通常由 program_a 输出的所有内容带到标准输出,并将其全部直接提供给 program_b,就像从标准输入中读取一样。

【讨论】:

  • 感谢您的澄清!
【解决方案2】:

在句法分组方面,&gt;&lt;优先级更高;也就是说,这两个命令是等价的:

sort < names | head
( sort < names ) | head

这两个也是:

ls | sort > out.txt
ls | ( sort > out.txt )

但是在顺序上,|是先执行的;所以,这个命令:

cat in.txt > out1.txt | cat > out2.txt

将填充out1.txt,而不是out2.txt,因为&gt; out1.txt是在|之后执行的,因此取代它(所以没有输出是发送到cat &gt; out2.txt)。

同样,这个命令:

cat < in1.txt | cat < in2.txt

将打印in2.txt,而不是in1.txt,因为&lt; in2.txt|之后执行,因此取代它(所以没有输入来自cat &lt; in1.txt)。

【讨论】:

  • @Kairan: cat in.txt &gt; out1.txt | cat &gt; out2.txt 表示( cat in.txt &gt; out1.txt ) | ( cat &gt; out2.txt ):它将cat in.txt &gt; out1.txt 的输出通过管道传输到cat &gt; out2.txt。但是cat in.txt &gt; out1.txt 没有 任何输出(因为它的输出到out1.txt),所以cat &gt; out2.txt 没有得到任何输入。你明白我的意思吗?
  • 声明But in terms of sequential ordering, | is performed first 不正确。管道和重定向都是从左到右完成的。考虑cd /foo 2&gt;&amp;1 &gt; out.txt | sed 's/foo/bar/' 这将 cd 的标准错误复制到其标准输出,然后将标准输出复制到 out.txt,然后将标准输出复制到管道。
  • @WilliamPursell:抱歉,您完全误会了。证据论证:如果这是真的,那么cat foo &gt; out1.txt &gt; out2.txtcat foo &gt; out1.txt | cat &gt; out2.txt 将是等价的,因为从左到右的处理会给出相同的结果。但事实上,前者写入out2.txt(因为&gt; out2.txt最后完成,所以胜出)而后者写入out1.txt(因为&gt; out1.txt| ...之后完成,所以胜出) .权威论证:[续]
  • [continued] §3.2.2 "Pipelines" of the Bash Reference Manual 明确表示,“管道中每个命令的输出通过管道连接到下一个命令的输入。也就是说,每个命令读取前一个命令的输出。此连接在命令指定的任何重定向之前执行(强调我的)。
  • 我的反对只是因为这个特定的例子并不合适,因为到 out2.txt 的重定向被应用于不同的命令。你完全正确,| 重定向必须首先发生。
【解决方案3】:

来自man bash(与其他引号一样):

SHELL GRAMMAR
   Simple Commands
       A simple command is a sequence of optional variable assignments followed by
       blank-separated words and redirections, and terminated  by  a  control
       operator. The first word specifies the command to be executed, and is
       passed as argument zero.  The remaining words are passed as arguments
       to the invoked command.

       The return value of a simple command is its exit status, or 128+n if
       the command is terminated by signal n.

   Pipelines
       A pipeline is a sequence of one or more commands separated by one of
       the control operators | or |&.  The format for a pipeline is:

              [time [-p]] [ ! ] command [ [|⎪|&] command2 ... ]

换句话说,一个(简单)命令可以有任意数量的重定向;您也可以将其用作管道的一部分。或者,换句话说,重定向比管道绑定得更紧密。

有几种方法可以解决这个问题(尽管它们很少是必要的或美观的):

1.你可以做一个“复合命令”并重定向到它:

 Compound Commands
   A compound command is one of the following:

   (list)  list is executed in a subshell environment (see
           COMMAND EXECUTION ENVIRONMENT below).  Variable
           assignments  and  builtin  commands  that  affect  the
           shell's environment do not remain in effect after the
           command completes.  The return status is the exit status of list.

   { list; }
          list  is  simply  executed  in the current shell environment.  list
          must be terminated with a newline or semicolon.  This is known as a
          group command. The return status is the exit status of list.  Note
          that unlike the metacharacters ( and ), { and } are reserved words
          and must occur where a reserved word is permitted to be recognized.
          Since they do not cause a word break, they must be separated from
          list by whitespace or another shell metacharacter.

所以:

$ echo foo > input
$ { cat | sed 's/^/I saw a line: /'; } < input
I saw a line: foo

2.您可以使用“进程替换”重定向到管道:

Process Substitution
   Process  substitution  is  supported on systems that support named pipes
   (FIFOs) or the /dev/fd method of naming open files.  It takes the form of
   <(list) or >(list).  The process list is run with its input or output
   connected to a FIFO or some file in /dev/fd.  The name of this file is
   passed as  an  argument  to  the  current  command  as the result of the
   expansion.  If the >(list) form is used, writing to the file will provide
   input for list.  If the <(list) form is used, the file passed as an argument
   should be read to obtain the output of list.

所以:

 rici@...$ cat > >(sed 's/^/I saw a line: /') < <(echo foo; echo bar)
 I saw a line: foo
 rici@...$ I saw a line: bar

(为什么提示在输出终止之前出现,以及如何处理它留作练习)。

【讨论】:

    【解决方案4】:

    这几乎是我阅读后所理解的(包括ruakh的回答)

    首先,如果你多次重定向,所有的重定向都会执行,但是只有最后一次的重定向才会生效(假设之前的重定向都没有出错)

    • 例如cat &lt; in1.txt &lt; in2.txt 等价于cat &lt; in2.txt,除非in1.txt 不存在,否则此命令将失败(因为先执行&lt; in1.txt

    • 类似地,cat in.txt &gt; out1.txt &gt; out2.txt,只有out2.txt 会包含out2.txt 的内容,但由于首先执行&gt; out1.txt,如果out1.txt 不存在,则会创建它。

    管道的作用是将上一个命令的stdout 连接到下一个命令的stdin,并且该连接位于任何其他重定向之前(来自Bash manual)。

    所以你可以想到

    cat in1.txt > out1.txt | cat > out2.txt
    

    作为

    cat in1.txt > pipe > out1.txt; cat < pipe > out2.txt
    

    并应用前面提到的多重重定向规则,我们可以将其简化为

    cat in1.txt > out1.txt; cat < pipe > out2.txt
    

    结果in1.txt的内容被复制到out1.txt,因为没有任何东西写入pipe


    使用 [ruak][3] 的另一个例子,
    cat < in1.txt | cat < in2.txt
    

    大致相当于

    cat > pipe < in1.txt; cat < pipe < in2.txt
    

    这是有效的

    cat > pipe < in1.txt; cat < in2.txt
    

    结果:这次是向pipe写入了一些东西,但是由于第二个cat是从in2.txt而不是pipe读取的,所以只打印了in2.txt的内容出去。如果pipe 位于同一侧(&gt;&lt;)重定向的中间,则会被重定向。

    【讨论】:

    • Re: "只有最后一个重定向才会生效,例如cat &lt; in1.txt &lt; in2.txt 相当于cat &lt; in2.txt": 这大部分是真的,但我确实有一个小问题:因为&lt; in1.txt 被执行(尽管后来被取代),如果in1.txt 不存在或无法读取,命令cat &lt; in1.txt &lt; in2.txt 将失败。
    • 感谢您的澄清。编辑了我的答案以添加这一点。
    【解决方案5】:

    &lt; 放在任何你喜欢的地方有点不正统,但完全合法,所以我更喜欢这样,因为它更好地说明了从左到右的数据流:

    <input.txt sort | head >output.txt
    

    您唯一无法做到这一点的是内置控制结构命令(forifwhile)。

    # Unfortunately, NOT LEGAL
    <input.txt  while read line; do ...; done
    

    请注意,所有这些都是等效的命令,但为避免混淆,您应该只使用第一个或最后一个:

    <input.txt grep -l foobar
    grep <input.txt -l foobar
    grep -l <input.txt foobar
    grep -l foobar <input.txt
    

    因为文件名必须始终紧跟在重定向运算符之后,所以我宁愿省略&lt; 和文件名之间的可选空格。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-10
      • 1970-01-01
      • 1970-01-01
      • 2012-08-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多