I want to make clear: When does pipe |
or redirection <
>
take precedence in a command?
This is my thought but I need confirmation this is how it works.
Example 1:
sort < names | head
The pipe runs first: names|head
then it sorts what is returned from names|head
Example 2:
ls | sort > out.txt
This one seems straight forward by testing, ls|sort
then redirects to out.txt
Example 3:
Fill in the blank? Can you have both a <
and a >
with a |
???
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.txt
ls | ( sort > out.txt )
But >
, <
, and |
are not "pure" computations — they have side-effects — so you can't think of them quite like mathematical operations, and in more complicated cases, you have to keep more things in mind than just syntactic grouping; the order in which things happens also matters. For example, these two are not equivalent:
some_command >out.txt 2>&1
some_command 2>&1 >out.txt
Within a simple command, the redirections are performed from left to right; so:
>out.txt 2>&1
means "first create or truncate out.txt
and connect standard output (file descriptor 1) to write to it, then connect standard error (file descriptor 2) to write to the same stream as standard output"; so the standard output and standard error of some_command
end up combined in out.txt
.2>&1 >out.txt
means "first connect standard error to write to the same stream as standard output, then create or truncate out.txt
and connect standard output to write to it"; so the standard output of some_command
goes to out.txt
, but the standard error of some_command
ends up going to the standard output of the complete command.In a pipeline, the pipe connections are performed before the redirections in the individual commands that make up the pipeline, which means that those redirections can supersede the pipe connections.
This means that something like this:
A.sh >A.txt 2>&1 | B.sh # weird
doesn't make a lot of sense, because A.sh >A.txt 2>&1
doesn't print anything to its standard output, so nothing flows over the pipe to B.sh; whereas something like this:
A.sh 2>&1 >A.txt | B.sh # OK
makes perfect sense: it sends A.sh's standard output to a file, and pipes A.sh's standard error over to B.sh's standard input.
If you want to try out different permutations for yourself, you might find it helpful to declare some helper functions that help you experiment:
# Print text on both stdout and stderr, with labels
# to help distinguish:
function multiprint() {
echo "[stdout] $*"
echo "[stderr] $*" 1>&2
}
# Copy stdin to stdout, with a custom label:
function preprint() {
local line
while IFS= read -r line ; do
echo "[$*] $line"
done
}
Here are some things you might try out:
multiprint X | preprint Y
multiprint X >/dev/null | preprint Y
multiprint X 3>&1 1>&2 2>&3 | preprint Y
(Or, better yet, make a guess about what they'll print, and then try them out to see if your guess was right!)