Shell Redirection and Pipelines
One objective, and most of it is muscle memory. The part that is not is the order of `>` and `2>&1`, which produces two different results from what looks like the same instruction - captured here side by side so the difference is visible rather than explained.
Essential Tools Guide 5 of 67 Beginner
- OSRHEL 10.0 (Coughlan)
- Kernel6.12.0-55.9.1.el10_0
- dnf4.20.0
- Flatpak1.16.0
- TimeAbout 14 min
- Reviewed23 August 2026
Written against the versions above. `&>file` is a bash extension and works everywhere bash is the shell, which on RHEL it is. In a strict POSIX `sh` script, `>file 2>&1` is the portable form and the one worth defaulting to.
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| RHCSA-A01 | 192.168.0.31 | RHEL 10.0 (Coughlan) | Practice node (graded) - spare /dev/sda | 2 Core | 4 GB | 50 GB + 15 GB |
Before you start
- A shell.
- The session writes and removes
~/redir.
-
Two streams, not one
Every command has two output channels, and they look identical on a terminal:
ls: cannot access '/etc/nosuchfile': No such file or directory /etc/hostnameRedirect stdout alone and the difference appears:
ls /etc/hostname /etc/nosuchfile > out.txt ls: cannot access '/etc/nosuchfile': No such file or directory --- stdout file: /etc/hostnameThe error stayed on the terminal.
>redirects file descriptor 1 (stdout) only; the error went to descriptor 2 (stderr), which was never touched.Send each to its own file and both are captured separately -
2>for stderr,>for stdout. That separation is the whole model: 1 is results, 2 is complaints, and every operator below is a way of routing those two numbers.bash Example session mkdir -p ~/redir && cd ~/redir && ls /etc/hostname /etc/nosuchfilels: cannot access '/etc/nosuchfile': No such file or directory/etc/hostname[exit 2]cd ~/redir && ls /etc/hostname /etc/nosuchfile > out.txt; echo "--- stdout file:"; cat out.txtls: cannot access '/etc/nosuchfile': No such file or directory--- stdout file:/etc/hostnamecd ~/redir && ls /etc/hostname /etc/nosuchfile 2> err.txt > out.txt; echo "--- stdout:"; cat out.txt; echo "--- stderr:"; cat err.txt--- stdout:/etc/hostname--- stderr:ls: cannot access '/etc/nosuchfile': No such file or directoryExpected resultThe error surviving a stdout redirect, then both captured separately.
Success conditionYou can route results and errors independently.
-
Overwrite, append, and refusing to clobber
two>truncates first. Theonewas written and then destroyed by the second command, and there is no warning.one two>>appends. On an exam that is the difference between adding a line to/etc/fstaband replacing the file with one line - a mistake that stops the machine booting and costs every other task.Bash can be told to refuse:
bash: b.txt: cannot overwrite existing file exit=1set -o noclobbermakes>fail on a file that exists, and>|overrides it for one command. It is not exam material, and it is a habit worth having on any machine you care about.bash Example session cd ~/redir && echo one > a.txt && echo two > a.txt && cat a.txttwocd ~/redir && echo one > b.txt && echo two >> b.txt && cat b.txtonetwocd ~/redir && set -o noclobber; echo three > b.txt; echo "exit=$?"; set +o noclobberbash: line 2: b.txt: cannot overwrite existing fileexit=1Expected resultTruncation, appending, and a refused overwrite.
Success conditionYou will not truncate a config file you meant to add a line to.
-
The ordering trap
These two look like the same instruction written two ways. They are not.
>file 2>&1- everything in the file:--- '>file 2>&1' captured: ls: cannot access '/etc/nosuchfile': No such file or directory /etc/hostname2>&1 >file- look where the error went:ls: cannot access '/etc/nosuchfile': No such file or directory --- '2>&1 >file' captured: /etc/hostnameThe error printed before the
captured:line - it went to the terminal, not the file. Only stdout was captured.The reason is that redirections are applied **left to right, and
2>&1copies where stdout points *at that moment*.** In the second form, stdout still points at the terminal when stderr is pointed at it;>filethen moves stdout to the file and leaves stderr behind. It is a copy of a destination, not a lasting link.&>filedoes the obvious thing in one token and cannot be got wrong:--- '&>file' captured: ls: cannot access '/etc/nosuchfile': No such file or directory /etc/hostnameRule of thumb:
2>&1goes last, or use&>.bash Example session cd ~/redir && ls /etc/hostname /etc/nosuchfile > both1.txt 2>&1; echo "--- '>file 2>&1' captured:"; cat both1.txt--- '>file 2>&1' captured:ls: cannot access '/etc/nosuchfile': No such file or directory/etc/hostnamecd ~/redir && ls /etc/hostname /etc/nosuchfile 2>&1 > both2.txt; echo "--- '2>&1 >file' captured:"; cat both2.txtls: cannot access '/etc/nosuchfile': No such file or directory--- '2>&1 >file' captured:/etc/hostnamecd ~/redir && ls /etc/hostname /etc/nosuchfile &> both3.txt; echo "--- '&>file' captured:"; cat both3.txt--- '&>file' captured:ls: cannot access '/etc/nosuchfile': No such file or directory/etc/hostnameExpected resultThe same command capturing different things depending on operator order.
Success conditionYou can read
2>&1and know which way round it must go. -
Discarding, and seeing output twice
/etc/hostname2>/dev/nullthrows errors away. Useful forfind / -name ...as a normal user, where permission-denied noise buries the results - and dangerous everywhere else, because it hides the message that explains the failure.teewrites to a file *and* passes output on:2 --- tee.txt holds: ls: cannot access '/etc/nosuchfile': No such file or directory /etc/hostnameThe
2iswc -lcounting what came through the pipe, while the file holds the same two lines. That is the point oftee: a copy without interrupting the pipeline.Its real use on an exam is writing a file that needs root when your shell is not root:
echo 'some line' | sudo tee -a /etc/somefilesudo echo 'x' > /etc/fdoes not work - the redirect is performed by your unprivileged shell before sudo ever runs.sudo teeis the fix, and-amakes it append rather than truncate.bash Example session cd ~/redir && ls /etc/hostname /etc/nosuchfile 2>/dev/null/etc/hostname[exit 2]cd ~/redir && ls /etc/hostname /etc/nosuchfile 2>&1 | tee tee.txt | wc -l; echo "--- tee.txt holds:"; cat tee.txt2--- tee.txt holds:ls: cannot access '/etc/nosuchfile': No such file or directory/etc/hostnamecd ~/redir && echo appended | tee -a tee.txt >/dev/null && tail -2 tee.txt/etc/hostnameappendedExpected resultErrors discarded, then output both counted and saved.
Success conditionYou can write a root-owned file from an unprivileged shell.
-
Feeding input in
adm bin chrony daemon ftpA pipeline:
cutsplits the field,sortorders it,headtruncates. Each program reads stdin and writes stdout, which is what makes them composable.<redirects a file *into* a command, and the difference from passing a filename is visible:30 --- versus: 30 /etc/passwdSame count, different output. With
<,wcreads stdin and has no filename to print; given the path it prints it. That matters in scripts that parse the output.Two more input forms.
<<<passes one string:HERE STRINGS FEED STDINand
<<passes a block until a marker - the here-document, which is how a multi-line file gets written in a script:apple cherry pearbash Example session cut -d: -f1 /etc/passwd | sort | head -5admavahibinchronycleviswc -l < /etc/passwd; echo "--- versus:"; wc -l /etc/passwd41--- versus:41 /etc/passwdtr 'a-z' 'A-Z' <<< "here strings feed stdin"HERE STRINGS FEED STDINsort <<'EOF'pearapplecherryEOFapplecherrypearExpected resultA pipeline, and three ways of supplying input.
Success conditionYou can build a pipeline and feed it from a file, a string or a block.
Troubleshooting
sudo echo text > /etc/filesays permission denied.Why: Your shell performs the redirect, not sudo.
Fix:
echo text | sudo tee -a /etc/file.Errors still appear on screen after redirecting to a file.
Why:
>only redirects stdout.Fix:
>file 2>&1or&>file, with2>&1last.A file was replaced when you meant to add to it.
Why:
>instead of>>.Fix:There is no undo. On config files, take a copy first -
cp /etc/fstab{,.bak}.A pipeline reports success although a stage failed.
Why: The exit status is the LAST command's.
Fix:
set -o pipefail, or check${PIPESTATUS[@]}.