Pipe | Redirection < > Precedence
In terms of syntactic grouping, >
and <
have higher precedence; that is, these two commands are equivalent:
sort < names | head( sort < names ) | head
as are these two:
ls | sort > out.txtls | ( sort > out.txt )
But in terms of sequential ordering, |
is performed first; so, this command:
cat in.txt > out1.txt | cat > out2.txt
will populate out1.txt
, not out2.txt
, because the > out1.txt
is performed after the |
, and therefore supersedes it (so no output is piped out to cat > out2.txt
).
Similarly, this command:
cat < in1.txt | cat < in2.txt
will print in2.txt
, not in1.txt
, because the < in2.txt
is performed after the |
, and therefore supersedes it (so no input is piped in from cat < in1.txt
).
From man bash
(as are the other quotes):
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 ... ]
In other words, you can have any number of redirections for a (simple) command; you can also use that as part of a pipeline. Or, put another way, redirection binds more tightly than pipe.
There are a couple of ways to get work around this (although they're rarely either necessary or aesthetic):
1.
You can make a "compound command" and redirect into it:
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.
So:
$ echo foo > input$ { cat | sed 's/^/I saw a line: /'; } < inputI saw a line: foo
2.
You can redirect to a pipe using "process substitution":
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.
So:
rici@...$ cat > >(sed 's/^/I saw a line: /') < <(echo foo; echo bar) I saw a line: foo rici@...$ I saw a line: bar
(Why the prompt appears before the output terminates, and what to do about it are left as exercises).
This is pretty much what I understand after doing some reading (including ruakh's answer)
First of all, if you redirect multiple times, all the redirections are performed, but only the last redirection will take effect (assuming none of the earlier redirections cause error)
e.g.
cat < in1.txt < in2.txt
is equivalent tocat < in2.txt
, unlessin1.txt
does not exist in which case this command will fail (since< in1.txt
is performed first)Similarly, with
cat in.txt > out1.txt > out2.txt
, onlyout2.txt
would contain the contents ofout2.txt
, but since> out1.txt
was performed first,out1.txt
would be created if it doesn't exist.
What pipe does is connect the stdout
of previous command to the stdin
of the next command, and that connection comes before any other redirections (from Bash manual).
So you can think of
cat in1.txt > out1.txt | cat > out2.txt
as
cat in1.txt > pipe > out1.txt; cat < pipe > out2.txt
And applying the multiple redirection rule mentioned before, we can simplify this to
cat in1.txt > out1.txt; cat < pipe > out2.txt
Result: The content of in1.txt
is copied to out1.txt
, since nothing was written to pipe
Using another of [ruakh][3]'s example,
cat < in1.txt | cat < in2.txt
is roughly equivalent to
cat > pipe < in1.txt; cat < pipe < in2.txt
which is effectively
cat > pipe < in1.txt; cat < in2.txt
Result: This time something is written to the pipe
, but since the second cat
reads from in2.txt
instead of pipe
, only the content of in2.txt
is printed out. If the pipe
is in the middle of the same side (>
or <
) redirection, it will be ingored.