Saving output, logging errors and feeding files into commands are everyday shell tasks.
Every process starts with three open file descriptors: 0 standard input, 1 standard output, 2 standard error. By default all three are the terminal. Redirection reconnects them before the command starts:
> filewrites stdout to a file, truncating it first;>> fileappends.2> filesends stderr to a file;2>> fileappends.2>&1makes fd 2 point wherever fd 1 currently points;&> fileis bash shorthand for "both to file".< filefeeds the file to stdin./dev/nulldiscards whatever is written to it.
Redirections are processed left to right, which makes order matter: cmd > out.txt 2>&1 sends both streams to the file, while cmd 2>&1 > out.txt first points stderr at the terminal (where stdout was at that moment) and only then moves stdout.
Keeping errors on stderr is a convention worth following in your own scripts too: echo "error: missing file" >&2. Then a caller can redirect your normal output to a file and still see the errors.
Beware sort data.txt > data.txt: the shell truncates the file before sort reads it, leaving it empty. Write to a temporary file and move it, or use sponge from moreutils. set -o noclobber makes > refuse to overwrite existing files.
Example
cd "$(mktemp -d)"
echo "first" > out.txt # create or truncate
echo "second" >> out.txt # append
cat out.txt # first / second
ls /nope 2> err.txt # the error goes to the file
cat err.txt # ls: cannot access '/nope': No such file or directory
ls /tmp /nope > all.txt 2>&1 # both streams into the file
ls /tmp /nope &> all.txt # bash shorthand for the same
wc -l < out.txt # 2 (stdin from a file: no name printed)
ls /nope 2>/dev/null || echo "quietly failed" # quietly failed
echo "error: something broke" >&2 # write to stderrRemember
- fd 0 stdin, 1 stdout, 2 stderr; > truncates, >> appends
- cmd > file 2>&1 captures both; order of redirections matters
- Send your own error messages to >&2
Watch out for
sort f > fempties the file: the shell truncates it before sort reads it.