CertGrid CertGrid
Hands-on Lab·Red Hat Certified System Administrator

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

&>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.

Every command on this page runs on RHCSA-A01.
Server NameIP AddressOSRolesCPURAMHDD
RHCSA-A01192.168.0.31RHEL 10.0Practice node (graded) - spare /dev/sda2 Core4 GB50 GB + 15 GB

This guide includes

Use this because most of this objective is muscle memory worth building once. This matters because every command has two output channels that look identical on screen - and the order of the redirections decides where each one ends up.

Before you start

  1. 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/hostname

    Redirect 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/hostname

    The 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 directory

    Expected resultThe error surviving a stdout redirect, then both captured separately.

    Success conditionYou can route results and errors independently.

  2. Overwrite, append, and refusing to clobber

    two

    > truncates first. The one was 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/fstab and 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=1

    set -o noclobber makes > 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=1

    Expected resultTruncation, appending, and a refused overwrite.

    Success conditionYou will not truncate a config file you meant to add a line to.

  3. 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/hostname

    2>&1 >file - look where the error went:

    ls: cannot access '/etc/nosuchfile': No such file or directory
    --- '2>&1 >file' captured:
    /etc/hostname

    The 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>&1 copies where stdout points *at that moment*.** In the second form, stdout still points at the terminal when stderr is pointed at it; >file then moves stdout to the file and leaves stderr behind. It is a copy of a destination, not a lasting link.

    &>file does the obvious thing in one token and cannot be got wrong:

    --- '&>file' captured:
    ls: cannot access '/etc/nosuchfile': No such file or directory
    /etc/hostname

    Rule of thumb: 2>&1 goes 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/hostname

    Expected resultThe same command capturing different things depending on operator order.

    Success conditionYou can read 2>&1 and know which way round it must go.

  4. Discarding, and seeing output twice

    /etc/hostname

    2>/dev/null throws errors away. Useful for find / -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.

    tee writes to a file *and* passes output on:

    2
    --- tee.txt holds:
    ls: cannot access '/etc/nosuchfile': No such file or directory
    /etc/hostname

    The 2 is wc -l counting what came through the pipe, while the file holds the same two lines. That is the point of tee: 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/somefile

    sudo echo 'x' > /etc/f does not work - the redirect is performed by your unprivileged shell before sudo ever runs. sudo tee is the fix, and -a makes 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/hostnameappended

    Expected resultErrors discarded, then output both counted and saved.

    Success conditionYou can write a root-owned file from an unprivileged shell.

  5. Feeding input in

    adm
    bin
    chrony
    daemon
    ftp

    A pipeline: cut splits the field, sort orders it, head truncates. 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/passwd

    Same count, different output. With <, wc reads 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 STDIN

    and << passes a block until a marker - the here-document, which is how a multi-line file gets written in a script:

    apple
    cherry
    pear
    bash 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'pearapplecherryEOFapplecherrypear

    Expected 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

Official sources