Pipes and Redirection
Connecting commands together and controlling where their input and output go - the core mechanism that makes small Unix tools composable into larger ones.
Every process starts with three open file descriptors: stdin (0, input), stdout (1, normal output), and stderr (2, error output). Redirection and pipes both work by rewiring these descriptors - redirection sends a descriptor to a file, a pipe sends it to another process.
Redirecting to and from files
command > output.txt # stdout to a file, overwriting it
command >> output.txt # stdout to a file, appending
command < input.txt # stdin from a file
command 2> errors.txt # stderr only
command > all.txt 2>&1 # stdout AND stderr, both to the same file
command &> all.txt # Bash shorthand for the line above
Order matters with 2>&1: it means "point fd 2 at wherever fd 1 currently
points," so it has to come after > redirects stdout, or it'll still
point at the terminal.
command > all.txt 2>&1 # correct: stderr follows stdout into all.txt
command 2>&1 > all.txt # wrong: stderr still goes to the terminal
Discard output entirely by redirecting to /dev/null, a special device
file that silently accepts and discards anything written to it:
noisy-command > /dev/null 2>&1
Pipes
A pipe (|) connects one command's stdout directly to the next command's
stdin, without ever touching disk:
ps aux | grep nginx | grep -v grep
www-data 1284 0.0 0.3 55120 9876 ? S 09:14 0:00 nginx: worker process
root 1281 0.0 0.2 55120 6544 ? Ss 09:14 0:00 nginx: master process
Chains like this are the standard Unix pattern: each tool (ps, grep,
and text-processing tools like sed and awk) does one thing, and pipes
compose them into something none of them do alone. See
Job Control for what happens to the processes
in a pipeline once it's running.
tee: split a stream in two
tee writes its input to a file and passes it through to stdout
unchanged - useful when you want to both see output live and save it:
long-build.sh | tee build.log
xargs: turning output into arguments
Pipes pass data as a stream, but many commands expect arguments, not
stdin. xargs bridges the two by reading stdin and building a command line
from it:
find . -name "*.tmp" | xargs rm
Be careful with filenames containing spaces or newlines - use -print0 and
xargs -0 for filenames that might not be simple:
find . -name "*.tmp" -print0 | xargs -0 rm
Heredocs and here-strings
A heredoc feeds multi-line input to a command's stdin, useful in scripts:
cat <<EOF > config.txt
host=localhost
port=8080
EOF
A here-string does the same for a single line, more concisely:
grep "port" <<< "$config_variable"
See Shell Scripting for using these inside
scripts, and Environment Variables for how
values like $config_variable get set in the first place.