ZetCode

Linux Redirection and Pipes

last modified October 23, 2026

Redirection and pipes control where commands read their input and where they write their output. Instead of the terminal, a command can read from a file and write to a file, and the output of one command can become the input of another. These features are provided by the shell, not by the individual commands, so they work the same way for every program.

Every process starts with three open file descriptors: standard input (stdin, 0), standard output (stdout, 1), and standard error (stderr, 2). By default, all three are connected to the terminal. The examples use Bash.

Redirecting output

The > operator sends standard output to a file.

echo "first" > out.txt
echo "second" > out.txt

The file is created if it does not exist. If it exists, it is truncated before the command runs, so the second command replaces the contents.

$ echo "first" > out.txt
$ cat out.txt
first
$ echo "second" > out.txt
$ cat out.txt
second

Appending output

The >> operator appends to the end of a file instead of overwriting it.

echo "third" >> out.txt

Appending is the usual way to write log files.

$ echo "third" >> out.txt
$ cat out.txt
second
third

Redirecting input

The < operator connects a file to standard input.

wc -l < words.txt
tr a-z A-Z < words.txt | head -n 3

When wc gets a file name, it prints the name with the count. With input redirection, it reads anonymous data from standard input and prints only the number. Some commands, such as tr, read only from standard input.

$ wc -l words.txt
7 words.txt
$ wc -l < words.txt
7
$ tr a-z A-Z < words.txt | head -n 3
SKY
FOREST
FALCON

Redirecting standard error

Error messages go to standard error, which is redirected with 2>, where 2 is the file descriptor number.

ls words.txt missing.txt 2> errors.txt
ls words.txt missing.txt > found.txt 2> errors.txt

The ls command writes the found file to standard output and the error about the missing one to standard error. Redirecting each stream to a different file separates results from errors. The > operator is a shorthand for 1>.

$ ls words.txt missing.txt
ls: cannot access 'missing.txt': No such file or directory
words.txt
$ ls words.txt missing.txt 2> errors.txt
words.txt
$ cat errors.txt
ls: cannot access 'missing.txt': No such file or directory
$ ls words.txt missing.txt > found.txt 2> errors.txt
$ cat found.txt
words.txt

Redirecting both streams

The 2>&1 construct duplicates standard error onto standard output, so both end up in the same place.

ls words.txt missing.txt > all.txt 2>&1
ls words.txt missing.txt 2>&1 > all.txt
ls words.txt missing.txt &> all.txt

Redirections are processed from left to right, so the order matters. In the first command, standard output goes to the file and then standard error is pointed to the same place. In the second, standard error is pointed to the terminal (where standard output was at that moment), and only then is standard output redirected, so the error is still printed. The &> operator is a Bash shorthand for the first form; &>> appends.

$ ls words.txt missing.txt > all.txt 2>&1
$ cat all.txt
ls: cannot access 'missing.txt': No such file or directory
words.txt
$ ls words.txt missing.txt 2>&1 > all.txt
ls: cannot access 'missing.txt': No such file or directory
$ cat all.txt
words.txt
$ ls words.txt missing.txt &> all.txt
$ cat all.txt
ls: cannot access 'missing.txt': No such file or directory
words.txt

Discarding output

The special file /dev/null discards everything written to it.

ls words.txt missing.txt 2> /dev/null
ls missing.txt > /dev/null 2>&1; echo "exit: $?"

The first command hides error messages. The second discards all output, which is useful when only the exit status matters; here it is 2, which ls uses for serious problems such as missing arguments.

$ ls words.txt missing.txt 2> /dev/null
words.txt
$ ls missing.txt > /dev/null 2>&1; echo "exit: $?"
exit: 2

Pipes

The pipe operator | connects the standard output of one command to the standard input of the next. The commands run concurrently.

sort words.txt | uniq | head -n 3
ls words.txt missing.txt | wc -l
ls words.txt missing.txt |& wc -l

The first pipeline sorts the words, removes duplicates, and shows the first three. A pipe passes only standard output, so in the second pipeline the error message goes to the terminal and wc counts one line. The |& operator, a shorthand for 2>&1 |, passes both streams. To save a copy of the data flowing through a pipe, use the tee command.

$ sort words.txt | uniq | head -n 3
bear
falcon
forest
$ ls words.txt missing.txt | wc -l
ls: cannot access 'missing.txt': No such file or directory
1
$ ls words.txt missing.txt |& wc -l
2

Here documents

A here document feeds lines of text written in the script to the standard input of a command, up to a delimiter line.

report.sh
#!/bin/bash

user="jano"
cat <<EOF
Report for $user
Generated: $(date -d 2026-10-23 +%F)
EOF

cat <<'EOF'
Literal text: $user $(date)
EOF

In the first here document, variables and command substitutions are expanded. When the delimiter is quoted, as in 'EOF', the text is passed literally. The delimiter can be any word; it must appear alone on the closing line.

indent.sh
#!/bin/bash

if true; then
	cat <<-EOF
	indented line one
	indented line two
	EOF
fi

With <<-, leading tab characters are removed from the lines and the closing delimiter, so the here document can be indented with the surrounding code. Only tabs are stripped, not spaces.

$ ./report.sh
Report for jano
Generated: 2026-10-23
Literal text: $user $(date)
$ ./indent.sh
indented line one
indented line two

Here strings

A here string, written with <<<, passes a single string to standard input.

wc -w <<< "an old falcon"
read -r first rest <<< "sky forest falcon"
tr a-z A-Z <<< "hello there"

A newline is appended to the string. Unlike echo ... | read, a here string lets read run in the current shell, so the variables are available afterwards.

$ wc -w <<< "an old falcon"
3
$ read -r first rest <<< "sky forest falcon"
$ echo "$first | $rest"
sky | forest falcon
$ tr a-z A-Z <<< "hello there"
HELLO THERE

Process substitution

Process substitution, written as <(command), makes the output of a command available as a file name.

diff <(sort a.txt) <(sort b.txt)
comm -12 <(sort a.txt) <(sort b.txt)

Commands like diff and comm expect two file names, so a single pipe is not enough. Bash runs each sort and passes special paths such as /dev/fd/63, from which the output can be read. The >(command) form works in the other direction and provides a file name that writes to a command.

$ printf "%s\n" sky owl falcon > a.txt; printf "%s\n" owl sky wolf > b.txt
$ diff <(sort a.txt) <(sort b.txt)
1d0
< falcon
3a3
> wolf
$ comm -12 <(sort a.txt) <(sort b.txt)
owl
sky
$ echo <(true)
/dev/fd/63

Protecting files from overwriting

The noclobber option prevents > from overwriting existing files.

set -o noclobber
echo "data" > out.txt
echo "data" >| out.txt

With noclobber set, the redirection fails for an existing file. The >| operator overrides the protection explicitly. The set +o noclobber command turns the option off.

$ set -o noclobber
$ echo "data" > out.txt
bash: out.txt: cannot overwrite existing file
$ echo "data" >| out.txt
$ cat out.txt
data

Redirection with exec

The exec builtin without a command applies redirections to the current shell. This opens additional file descriptors or redirects all output of a script.

logging.sh
#!/bin/bash

exec 3> app.log

echo "starting" >&3
echo "visible on terminal"
echo "finished" >&3

exec 3>&-

cat app.log

We open file descriptor 3 for writing to app.log, send selected messages to it with >&3, and close it with 3>&-. Normal output still goes to the terminal.

whole.sh
#!/bin/bash

exec > run.log 2>&1

echo "step 1"
ls /nonexistent
echo "step 2"

Here, exec redirects both standard output and standard error of the rest of the script to run.log, so nothing appears on the terminal.

$ ./logging.sh
visible on terminal
starting
finished
$ ./whole.sh
$ cat run.log
step 1
ls: cannot access '/nonexistent': No such file or directory
step 2

Reading and writing the same file

A common mistake is to redirect the output of a command to the file it reads.

sort words.txt > words.txt

The shell truncates words.txt before sort starts, so sort reads an empty file and the data is lost. Write to a temporary file and rename it, or use an option for in-place editing, such as sort -o words.txt words.txt or sed -i.

$ sort words.txt > words.txt
$ wc -l words.txt
0 words.txt

Best practices

Source

Bash manual: Redirections

In this article, we have used output, input, and error redirection, pipes, here documents and here strings, process substitution, and exec to control the input and output of commands.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Linux tutorials.