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 traptee, here-documents<<EOFand here-strings<<<- Recording a whole session with
script, and logging withloggerandexec >> log
Three streams
Every program is born with three “pipes” already attached:
| Number | Name | What flows through it | Normally connected to |
|---|---|---|---|
0 | stdin | Input the program reads | Your keyboard |
1 | stdout | The normal results | Your screen |
2 | stderr | Error messages and warnings | Your 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.
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 write | It means |
|---|---|
cmd > file | stdout into file, replacing what was there (same as 1>) |
cmd >> file | stdout added to the end of file |
cmd 2> file | stderr into file |
cmd > file 2>&1 | both into file. 2>&1 means “send 2 to wherever 1 goes right now” |
cmd &> file | bash's shortcut for the line above (not in plain sh, see lesson 14) |
cmd 2>/dev/null | throw the errors away (/dev/null is the black hole from Linux Basics, lesson 7) |
cmd >/dev/null 2>&1 | total silence. Only the exit code $? is left |
echo "oops" >&2 | write your message to stderr (for error messages in scripts) |
cmd < file | stdin comes from file instead of the keyboard |
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
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:
cat > note.txt <<EOF Made by $(whoami) on $(hostname) Home folder: $HOME EOF
EOFis just a marker word. Any word works, as long as the last line matches it exactly.- With
<<EOF,$VARand$( )inside are filled in. With quotes,<<'EOF', everything stays exactly as typed. Use that when you're writing a script that contains$signs. <<-EOFignores leading tabs, so the text can be indented inside scripts.- A here-string is the one-line version:
tr a-z A-Z <<< "hello".
To write a root-owned file this way, combine it with tee: sudo tee /etc/motd <<'EOF' … EOF.
Record a whole session: script
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
logger -t mybackup "Backup finished OK" journalctl -t mybackup sudo tail /var/log/messages
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?
✓ Two separate streams. Redirect each one where you want it.
2. Which one puts both results and errors into all.txt?
✓ Left to right: first stdout goes to the file, then stderr follows it. Option a sends the errors to the screen, and option b makes the two streams overwrite each other.
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?
✓ Quoting the marker word turns off all $ expansion inside.