管| 重定向<>优先级

Kai*_*ran 22 unix bash redirect command pipe

我想弄清楚什么时候管道 或重定向<>在命令中优先?

这是我的想法,但需要确认这是它的工作原理.

例1:

sort < names | head
The pipe runs first:  names|head   then it sorts what is returned from names|head
Run Code Online (Sandbox Code Playgroud)

例2:

ls | sort > out.txt
This one seems straight forward by testing, ls|sort then redirects to out.txt
Run Code Online (Sandbox Code Playgroud)

例3:

Fill in the blank?  Can you have both a < and a > with a | ???
Run Code Online (Sandbox Code Playgroud)

rua*_*akh 22

在句法分组方面,><具有更高的优先级; 也就是说,这两个命令是等价的:

sort < names | head
( sort < names ) | head
Run Code Online (Sandbox Code Playgroud)

这两个是:

ls | sort > out.txt
ls | ( sort > out.txt )
Run Code Online (Sandbox Code Playgroud)

但就顺序排序而言,|首先执行; 所以,这个命令:

cat in.txt > out1.txt | cat > out2.txt
Run Code Online (Sandbox Code Playgroud)

将填充out1.txt,而不是out2.txt因为> out1.txt它在之后执行|,因此取代它(因此没有输出管道输出cat > out2.txt).

同样,这个命令:

cat < in1.txt | cat < in2.txt
Run Code Online (Sandbox Code Playgroud)

将打印in2.txt,而不是in1.txt,因为它< in2.txt是在之后执行|,因此取代它(所以没有输入管道输入cat < in1.txt).

  • 声明`但就顺序排序而言,| 首先执行`是不正确的.管道和重定向都是从左到右完成的.考虑`cd/foo 2>&1> out.txt | sed's/foo/bar /'`这将cd的stderr复制到它的stdout上,然后将stdout复制到out.txt然后将stdout复制到管道. (2认同)
  • @WilliamPursell:抱歉,但你完全错了。证据论证:如果这是真的,那么 `cat foo &gt; out1.txt &gt; out2.txt` 和 `cat foo &gt; out1.txt | cat &gt; out2.txt` 是等效的,因为从左到右处理会给出相同的结果。但事实上,前者写入 `out2.txt` (因为 `&gt; out2.txt` 最后完成,所以胜出),而后者写入 `out1.txt` (因为 `&gt; out1.txt` 是在`| ...`之后完成,所以胜出)。权威论证:*[续]* (2认同)
  • *[续]* [*Bash 参考手册*的§3.2.2“管道”](http://www.gnu.org/software/bash/manual/bashref.html#Pipelines) 明确指出,“输出管道中的每个命令都通过管道连接到下一个命令的输入。也就是说,每个命令都会读取前一个命令的输出。此连接在该命令指定的任何重定向之前执行“(强调我的) 。 (2认同)
  • 我的反对意见仅仅是这个特定的例子并不合适,因为重定向到 out2.txt 被应用于不同的命令。你是完全正确的`|` 重定向必须首先发生。 (2认同)

ric*_*ici 14

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 ... ]
Run Code Online (Sandbox Code Playgroud)

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

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

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.
Run Code Online (Sandbox Code Playgroud)

所以:

$ echo foo > input
$ { cat | sed 's/^/I saw a line: /'; } < input
I saw a line: foo
Run Code Online (Sandbox Code Playgroud)

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.
Run Code Online (Sandbox Code Playgroud)

所以:

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

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


dou*_*own 5

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

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

  • egcat < in1.txt < in2.txt等价于cat < in2.txt, 除非in1.txt不存在,在这种情况下此命令将失败(因为< in1.txt首先执行)

  • 类似地, with cat in.txt > out1.txt > out2.txt, onlyout2.txt将包含 的内容out2.txt,但由于> out1.txt首先执行,out1.txt如果它不存在,则将被创建。

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

所以你可以想到

cat in1.txt > out1.txt | cat > out2.txt
Run Code Online (Sandbox Code Playgroud)

作为

cat in1.txt > pipe > out1.txt; cat < pipe > out2.txt
Run Code Online (Sandbox Code Playgroud)

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

cat in1.txt > out1.txt; cat < pipe > out2.txt
Run Code Online (Sandbox Code Playgroud)

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


使用另一个 [ruakh][3] 的例子,
cat < in1.txt | cat < in2.txt
Run Code Online (Sandbox Code Playgroud)

大致相当于

cat > pipe < in1.txt; cat < pipe < in2.txt
Run Code Online (Sandbox Code Playgroud)

这是有效的

cat > pipe < in1.txt; cat < in2.txt
Run Code Online (Sandbox Code Playgroud)

结果:这次有些东西被写入pipe,但由于第二个cat是从in2.txt而不是pipe,所以只in2.txt打印出的内容。如果pipe是在同一侧(><)重定向的中间,它将被 ingored。