Linux Sysadmin · Lesson 11 · 25 min

Capturing output

A command's output usually flashes past on the screen and it's gone. Admins need it kept: in a file to send to someone, in a log to read tomorrow, or thrown away on purpose. This lesson is about steering output wherever you want it.

You will learn

  • The three streams: stdin, stdout and stderr (0, 1, 2)
  • > >> 2> 2>&1 &> /dev/null, and the order trap
  • tee, here-documents <<EOF and here-strings <<<
  • Recording a whole session with script, and logging with logger and exec >> log

Three streams

Every program is born with three “pipes” already attached:

NumberNameWhat flows through itNormally connected to
0stdinInput the program readsYour keyboard
1stdoutThe normal resultsYour screen
2stderrError messages and warningsYour screen, too

Results and errors land on the same screen, so they look alike, but they're separate streams. That's why you can save the results and still see the errors, or the other way round.

Same on both
ls /etc/hostname /nope                # one result, one error
ls /etc/hostname /nope > out.txt      # the result goes to the file… the error still shows!
ls /etc/hostname /nope 2> err.txt     # now only the error goes to a file

Redirections: the whole set

You writeIt means
cmd > filestdout into file, replacing what was there (same as 1>)
cmd >> filestdout added to the end of file
cmd 2> filestderr into file
cmd > file 2>&1both into file. 2>&1 means “send 2 to wherever 1 goes right now”
cmd &> filebash's shortcut for the line above (not in plain sh, see lesson 14)
cmd 2>/dev/nullthrow the errors away (/dev/null is the black hole from Linux Basics, lesson 7)
cmd >/dev/null 2>&1total silence. Only the exit code $? is left
echo "oops" >&2write your message to stderr (for error messages in scripts)
cmd < filestdin comes from file instead of the keyboard
The order trap

Bash reads redirections left to right. In ls /nope 2>&1 > all.txt, stderr is sent to wherever stdout was pointing at that moment (the screen), and only then is stdout moved to the file. The error still lands on your screen. Always write the file first: > all.txt 2>&1.

Silencing errors doesn't make them go away: ls /nope 2>/dev/null; echo $? still prints 2. Scripts test $?, not the text (lesson 13).

tee: see it and save it

Same on both
df -h | tee disk.txt                     # on screen AND in the file
date | tee -a disk.txt                   # -a = add to the end
sudo dnf upgrade 2>&1 | tee upgrade.log   # keep a record of a big job, errors included
echo "hi" | sudo tee /root/note.txt      # the sudo trick from the sudo lesson

A pipe only carries stdout. That's why the upgrade line adds 2>&1 before the |, so the errors make it into the log too.

Here-documents: type a whole file

A here-document feeds several lines into a command. It's the classic way to create a config file or a script from the terminal, with no editor needed. Type the first line, press Enter, and the prompt changes to > until you type the end word:

Same on both
cat > note.txt <<EOF
Made by $(whoami) on $(hostname)
Home folder: $HOME
EOF

To write a root-owned file this way, combine it with tee: sudo tee /etc/motd <<'EOF' … EOF.

Record a whole session: script

Same on both
script session.log      # start recording everything on screen
…                       # work normally
exit                    # stop; session.log now holds it all
script -a session.log   # -a = add to an existing recording

This is great for homework proof, bug reports and “what exactly did I type last Tuesday?” It's in util-linux on Rocky and bsdutils on Ubuntu, and preinstalled on both. Your SSH app can do this too: PuTTY has Session → Logging, and iTerm2 has automatic session logs (Linux Basics, lesson 4).

Output for later: logs

logger: write to the system log

Rocky / RHEL
logger -t mybackup "Backup finished OK"
journalctl -t mybackup
sudo tail /var/log/messages
Ubuntu / Debian
logger -t mybackup "Backup finished OK"
journalctl -t mybackup
tail /var/log/syslog

Your scripts' messages end up in the same place as everything else, with a time stamp and a tag you can search for (lesson 4). Add -p user.err to mark something as an error.

Send a whole script's output to a log

Put this near the top of a script, and everything after it (results and errors) goes into the log instead of the screen:

#!/bin/bash
exec >> /home/student/nightly.log 2>&1
echo "=== started $(date) ==="
…

That's perfect for cron jobs (lesson 5), whose output would otherwise vanish into mail nobody reads.

Try it 🎥

Quick check

1. You run find / -name "*.conf" > results.txt and your screen fills with “Permission denied”. Why?

2. Which one puts both results and errors into all.txt?

3. You're writing a script with a here-document, and the text contains $HOME which must stay exactly like that. What do you write?

Finished the missions and the quiz? Mark it done to track your progress.