Redirection and File Descriptors
By the end of this lesson you'll understand the three standard streams (stdin, stdout, stderr), how to send output to files with > and >>, how to read input from a file with <, how to separate and combine stdout and stderr (including the famous 2>&1 ordering trap), how to discard output with /dev/null, and how to use here-documents, here-strings, and tee.
Learn Redirection and File Descriptors in our free Command Line course — an interactive lesson with worked examples, a practice exercise and a quick reference.
Part of the free Cli course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn
1. The Three Standard Streams
Every command is born with three open streams, each identified by a number: 0 is stdin (the input it reads), 1 is stdout (its normal output), and 2 is stderr (its error messages). By default all three are wired to your terminal, which is why normal output and errors appear mixed together — but they are genuinely separate channels, and that separation is what every redirection below exploits.
2. Sending stdout to a File: > and >>
The > operator redirects stdout into a file. It truncates the file first (emptying it, or creating it if it does not exist), so the file ends up containing only what that one command wrote. The >> operator instead appends to the end of the file, preserving what was already there — perfect for a growing log.
3. Reading stdin from a File: <
The < operator redirects stdin so a command reads from a file instead of the keyboard. Many tools ( sort , wc , grep ) read whatever arrives on stdin, so sort < file sorts the file's lines. This is subtly different from passing the filename as an argument: with < the shell opens the file and hands the command an already-open input stream.
4. Errors and the 2>&1 Ordering Trap
Use 2> to redirect stderr alone. To merge the streams, 2>&1 tells the shell "send fd 2 to wherever fd 1 points right now " — the &1 means the current target of stdout, not a file named 1 . Because redirections are applied left to right , command > file 2>&1 sends both to the file, but the reversed command 2>&1 > file sends only stdout to the file and leaves stderr on the terminal — the classic gotcha.
5. Discarding Output: /dev/null and &>
/dev/null is the system's discard sink: anything written to it is thrown away. Redirect to it to silence streams you do not care about — 2> /dev/null hides errors, > /dev/null hides normal output. The bash shorthand &> sends both stdout and stderr at once, so &> /dev/null runs a command completely silently (it is the same as > /dev/null 2>&1 ). The command still runs and still sets $? .
6. Here-Documents, Here-Strings, and tee
A here-document ( <<EOF ... EOF ) feeds several literal lines into a command as stdin — ideal for writing a config file with cat > file <<EOF . Quote the delimiter ( <<'EOF' ) to switch off variable and command expansion. A here-string ( <<< "text" ) feeds a single string as stdin without a temp file. And tee duplicates stdin: it writes to a file and passes the text through to stdout, with -a to append.
🎯 Your Turn: append without erasing
Fill in the one blank marked ___ using the # 👉 hint so the second line is added rather than overwriting the file, then run it and check the Output panel matches.
🎯 Your Turn: capture both streams
Send both the normal output and the error message into one file. Put the redirection in the correct order, fill in the blank, then run it.
Pro Tips
- 💡 Remember the order rule as "redirect, then merge". Put > file first and 2>&1 last so stderr inherits the file. Reversed, 2>&1 > file only captures stdout.
- 💡 &> file and &>> file are bash shorthands for both streams (truncate / append). They are clearer than > file 2>&1 when you do not need portability to plain sh .
- 💡 Use tee to watch and save at once: ./build.sh 2>&1 | tee build.log shows the run live and keeps a full copy (errors included) in the log.
- 💡 Quote the heredoc delimiter ( <<'EOF' ) whenever the body contains literal $ signs — for example when writing a script or a Dockerfile — so nothing gets expanded by accident.
Common Errors (and the fix)
- I redirected with > and lost my file's contents — a single > truncates before writing. Use >> to append instead.
- My errors still print to the screen even though I wrote 2>&1 > file — the order is backwards. 2>&1 copied stdout's target (the terminal) before > file moved stdout. Write > file 2>&1 so both go to the file.
- I expected a file named 1 — 2>1 (no ampersand) literally creates a file called 1 . You almost always want 2>&1 , where &1 means "the current target of fd 1".
- My here-document never ends / "unexpected end of file" — the closing delimiter must be on its own line with nothing after it (and, by default, no leading spaces). Make the final EOF match the opening word exactly.
- $variables in my heredoc came out literally — you quoted the delimiter ( <<'EOF' ), which disables expansion. Use an unquoted <<EOF if you want $name expanded.
📋 Quick Reference
Task
Operator
Notes
Redirect stdout (truncate)
cmd > file
Empties file first (fd 1)
Append stdout
cmd >> file
Keeps existing contents
Redirect stdin
cmd < file
Read file as input (fd 0)
Redirect stderr
cmd 2> file
Errors only (fd 2)
Merge stderr into stdout
cmd > file 2>&1
Order matters; both to file
Both streams (bash)
cmd &> file
Same as > file 2>&1
Discard output
cmd > /dev/null
2> for errors, &> for both
Here-document
cmd <<EOF ... EOF
Quote 'EOF' to stop expansion
Here-string
cmd <<< "text"
One string as stdin
Save and pass through
cmd | tee file
-a appends to file
Frequently Asked Questions
Mini-Challenge: capture a build log
No blanks this time — just a brief and an outline. Combine a here-document, stream merging with the right order, /dev/null, and tee into a few commands that demonstrate everything from this lesson, and check your output against the hints in the comments.
🎉 Lesson Complete!
- ✅ The three streams are 0 stdin, 1 stdout, and 2 stderr
- ✅ > truncates and >> appends stdout; < reads stdin from a file
- ✅ 2> redirects errors; > file 2>&1 captures both, but 2>&1 > file does not (order matters)
- ✅ /dev/null discards output and &> is bash shorthand for both streams
- ✅ Here-documents and here-strings feed stdin, and tee writes to a file while passing text through
- ✅ Next lesson: Finding Files with find and xargs — locate files by name, type, and time, then act on the results
Practice quiz
Which file descriptor number is standard error (stderr)?
- 2
- 1
- 3
- 0
Answer: 2. By convention 0 is stdin, 1 is stdout, and 2 is stderr. So stderr is file descriptor 2.
What does the operator > do when you write command > file ?
- Reads the file as stdin for the command
- Redirects stderr into the file
- Truncates (or creates) the file and writes stdout into it
- Appends stdout to the end of the file
Answer: Truncates (or creates) the file and writes stdout into it. A single > redirects stdout to the file, truncating it first (or creating it if it does not exist). Use >> to append instead.
How do you APPEND stdout to an existing file without erasing its contents?
- command < file
- command >> file
- command 2> file
- command > file
Answer: command >> file. The >> operator appends stdout to the end of the file. A single > would truncate the file first.
What does 2>&1 mean?
- Discard both stdout and stderr
- Send stdin to stdout
- Swap stdout and stderr permanently
- Send file descriptor 2 (stderr) to wherever file descriptor 1 (stdout) currently points
Answer: Send file descriptor 2 (stderr) to wherever file descriptor 1 (stdout) currently points. 2>&1 makes stderr (fd 2) go to the same place stdout (fd 1) is pointing AT THAT MOMENT. The &1 means the current target of fd 1, not a file literally named 1.
Where does stderr end up in command 2>&1 > file ?
- stdout goes to the file and stderr goes to the original terminal
- Both go to the terminal
- stderr goes to the file and stdout goes to the terminal
- Both stdout and stderr go to the file
Answer: stdout goes to the file and stderr goes to the original terminal. Redirections are processed left to right. 2>&1 copies stdout's target FIRST (still the terminal), then > file moves stdout to the file. So stderr stays on the terminal and only stdout lands in the file.
Which redirection feeds the contents of a file into a command as its standard input?
- command | file
- command < file
- command > file
- command >> file
Answer: command < file. The < operator redirects stdin so the command reads from the file instead of the keyboard.
What is /dev/null used for in redirection?
- A file that stores all errors permanently
- The default log file for the shell
- A device that prints to the screen
- A discard sink: anything written to it is thrown away
Answer: A discard sink: anything written to it is thrown away. /dev/null is the null device. Writing to it discards the data, so command 2> /dev/null silently throws away error messages.
What does the bash shorthand &> do, as in command &> file ?
- Appends stdout to the file
- Reads the file as stdin
- Sends BOTH stdout and stderr to the file
- Runs the command in the background
Answer: Sends BOTH stdout and stderr to the file. In bash &> file is shorthand for sending both stdout and stderr to the file. It is equivalent to > file 2>&1.
What does a here-string command <<< "text" do?
- Creates a multi-line here-document
- Feeds the single string text into the command as standard input
- Redirects stderr to a string
- Appends text to a file
Answer: Feeds the single string text into the command as standard input. A here-string <<< feeds the given word or quoted string into the command on stdin, like a one-line here-document.
In a here-document, how do you DISABLE variable and command expansion inside the body?
- Quote the delimiter, for example <<'EOF'
- Add 2>&1 after the delimiter
- Use >> instead of <<
- Use <<- instead of <<
Answer: Quote the delimiter, for example <<'EOF'. Quoting the delimiter, as in <<'EOF', turns off all expansion so $variables and command substitutions are written literally. An unquoted <<EOF expands them.