Chapter 5: Text, editors and the shell environment

Redirection and pipes

Every command reads from one stream and writes to two: its output, and its error messages. By default all three are connected to your terminal. Redirection reconnects them to files, and a pipe connects one command's output to the next command's input, which is how small commands combine into powerful ones.

Three streams

NumberNameDefaultUsed for
0standard input (stdin)the keyboarddata the command reads
1standard output (stdout)the terminalthe command's normal output
2standard error (stderr)the terminalerror and warning messages

Output and errors both appear on your screen, so they look the same. They aren't: here ls prints one line to each stream.

[student@servera ~]$ ls /etc/hostname /etc/nothere
ls: cannot access '/etc/nothere': No such file or directory
/etc/hostname

Select an operator to see where each stream goes:

Where the three streams go: Select an operator to see where stdin, stdout and stderr are connected.

Send output to a file

OperatorEffect
> filestdout to file, replacing what was in it
>> filestdout to file, added to the end
2> filestderr to file
2>> filestderr to file, added to the end
&> filestdout and stderr to the same file
> file 2>&1the same, the older way to write it
< filestdin from file

Redirect the output and only the error is left on the screen; redirect the error and only the output is:

[student@servera ~]$ ls /etc/hostname /etc/nothere > out.txt
ls: cannot access '/etc/nothere': No such file or directory
[student@servera ~]$ cat out.txt
/etc/hostname
[student@servera ~]$ ls /etc/hostname /etc/nothere 2> err.txt
/etc/hostname
[student@servera ~]$ cat err.txt
ls: cannot access '/etc/nothere': No such file or directory

>> keeps what is already there, so it is the one for logs:

[student@servera ~]$ date >> log.txt
[student@servera ~]$ date >> log.txt
[student@servera ~]$ cat log.txt
Sat Oct  3 10:17:40 AM UTC 2026
Sat Oct  3 10:17:40 AM UTC 2026

Throw output away: /dev/null

/dev/null is a special file that discards everything written to it. Its classic use is hiding expected error messages. As an ordinary user, searching /etc produces a screenful of Permission denied lines mixed in with the results:

[student@servera ~]$ find /etc -name "*.repo"
find: ‘/etc/firewalld’: Permission denied
find: ‘/etc/sudoers.d’: Permission denied
...output omitted...
/etc/yum.repos.d/rocky-devel.repo
/etc/yum.repos.d/rocky.repo
...output omitted...
[student@servera ~]$ find /etc -name "*.repo" 2> /dev/null
/etc/yum.repos.d/rocky-devel.repo
/etc/yum.repos.d/rocky.repo
/etc/yum.repos.d/rocky-addons.repo
/etc/yum.repos.d/rocky-extras.repo
/etc/yum.repos.d/rocky-security.repo

Chapter 17 covers find itself. Only discard errors you understand: a message you throw away can be the one that explains a problem.

Order matters with 2>&1

2>&1 means "send stderr wherever stdout goes right now". Redirections are applied left to right, so these two differ:

[student@servera ~]$ find /etc -name "*.repo" > repos.txt 2>&1
[student@servera ~]$ wc -l repos.txt
18 repos.txt
[student@servera ~]$ find /etc -name "*.repo" 2>&1 > repos.txt | head -2
find: ‘/etc/firewalld’: Permission denied
find: ‘/etc/sudoers.d’: Permission denied

In the first, stdout goes to the file, then stderr follows it there: 13 errors and 5 results. In the second, stderr is pointed at where stdout went before stdout moved to the file, so the errors still flow down the pipe. &> avoids the puzzle when you want both in one file.

Pipes

A pipe, |, connects the stdout of the command on its left to the stdin of the command on its right. Each command does one small job, and together they answer questions no single command can:

[student@servera ~]$ ls /etc | wc -l
172
[student@servera ~]$ ls -t /var/log | head -3
messages
secure
lastlog
[student@servera ~]$ ls -l /usr/bin | sort -k5 -n | tail -3
-rwxr-xr-x. 1 root root 1452256 Sep 10 15:05 vi
-rwxr-xr-x. 1 root root 1651920 Apr  3  2026 cyrusbdb2current
-rwxr-xr-x. 1 root root 4028400 Sep 10 15:05 vim

Only stdout travels through a pipe; errors still go to the terminal unless you redirect them too. Any command that reads stdin can sit on the right: less, wc, sort, head, tail, and above all grep, the subject of the next lesson.

tee: save a copy on the way

tee copies its input to a file and passes it on, like a T-junction in a pipe. Use it to keep a record while still seeing the output, or to save an intermediate stage of a pipeline:

[student@servera ~]$ ls /etc | tee etc-list.txt | wc -l
172
[student@servera ~]$ head -n 2 etc-list.txt
adjtime
aliases
[student@servera ~]$ echo "hostname: $(hostname)" | tee -a info.txt
hostname: servera.lab.example.com

tee -a appends instead of replacing.

Try it

Check your understanding