Redirection is one of those Bash fundamentals that quietly powers almost everything else you do in the shell — logging, saving command results, suppressing noisy output, combining error messages with regular output. I use it constantly without even thinking about it anymore, but I remember when the difference between >, >>, and 2> felt like a genuinely confusing wall to get past. In this article, I’ll walk through redirection from the ground up, covering standard output, standard error, standard input, and some of the more advanced tricks that make scripts far more controllable.
The Three Standard Streams
Before diving into redirection syntax, it helps to know that every command in Bash has access to three standard streams:
- stdin (0) — standard input, where a command reads input from
- stdout (1) — standard output, where a command sends its normal output
- stderr (2) — standard error, where a command sends error messages
By default, all three are connected to your terminal — stdin reads from your keyboard, and stdout/stderr both print to your screen. Redirection lets you change where these streams point.
Redirecting Standard Output with >
echo "Hello, world!" > output.txt
This sends the output of echo into output.txt instead of the screen. If output.txt already exists, it gets overwritten (truncated to zero length first).
Appending Output with >>
echo "Another line" >> output.txt
Instead of overwriting, >> adds the new output to the end of the existing file, preserving whatever was already there.
Redirecting Standard Error with 2>
ls nonexistent_folder 2> errors.txt
This sends any error messages (like “No such file or directory”) into errors.txt, while normal output still goes to the screen.
Redirecting Both stdout and stderr
There are a couple of ways to capture both streams into the same file:
command > combined.txt 2>&1
The order matters here: this first redirects stdout to combined.txt, then redirects stderr (2) to wherever stdout is currently pointing (&1, which is now the file). If you reversed the order (2>&1 > combined.txt), stderr would still go to the terminal, because at the time 2>&1 runs, stdout hasn’t been redirected yet.
Bash also provides a shorthand for this common pattern:
command &> combined.txt
This achieves the same result more concisely, redirecting both streams to the file in one step.
Discarding Output Entirely
Sometimes you don’t want output at all — just suppress it. The special file /dev/null acts as a black hole that discards anything written to it:
command > /dev/null 2>&1
This is extremely common in scripts and cron jobs where you only care about a command’s exit status, not its output.
Redirecting Standard Input with <
sort < unsorted.txt
This feeds the contents of unsorted.txt into sort as its input, rather than sort reading from your keyboard.
Combining Input and Output Redirection
sort < unsorted.txt > sorted.txt
This reads from unsorted.txt, sorts it, and writes the result into sorted.txt.
Here Documents (<<)
A here document lets you feed multi-line input directly into a command:
cat << EOF
Line one
Line two
Line three
EOF
This is especially useful for generating multi-line text, like config files or email bodies, without needing multiple echo statements.
Here Strings (<<<)
A here string is a simpler variant for feeding a single string as input:
grep "hello" <<< "hello world"
This passes “hello world” directly as input to grep, without needing an intermediate file or echo piped in.
Redirecting to Multiple Destinations with tee
While not a redirection operator itself, tee is often used alongside redirection to send output to both a file and the screen simultaneously:
command | tee output.txt
Understanding File Descriptors
Every open file, pipe, or stream in a running process is tracked using a small integer called a file descriptor. By default:
0= stdin1= stdout2= stderr
You can also open custom file descriptors for more advanced use cases:
exec 3> custom_log.txt
echo "This goes to fd 3" >&3
exec 3>&- # close file descriptor 3
This is more advanced territory, but it’s useful in scripts that need to manage multiple log streams simultaneously.
How Redirection Works Internally
When Bash processes a redirection operator, it uses the open() system call to get a file descriptor pointing to the target file (creating it if necessary, and truncating it for > or seeking to the end for >>). It then uses dup2() to make the command’s stdout (or stderr, or stdin) point to that same file descriptor before the command actually runs. This all happens before the command itself starts executing — which is why redirection can be placed anywhere in a command line (> output.txt echo "hello" is technically valid, though unusual to write that way) and why a command has no idea, from its own perspective, whether its output is going to a terminal, a file, or another program. As far as the command is concerned, it’s just writing to file descriptor 1; Bash has already handled the plumbing of where that descriptor actually points.
Real-World Use Cases
Logging a script’s output to a file while still seeing it on screen:
#!/bin/bash
exec > >(tee -a script.log) 2>&1
echo "This appears both on screen and in script.log"
Silencing a noisy command in a cron job:
0 3 * * * /usr/local/bin/backup.sh > /dev/null 2>&1
Separating normal output from error output for later review:
./deploy.sh > deploy_output.log 2> deploy_errors.log
Automation Example: Full Logging Wrapper
#!/bin/bash
LOG_FILE="/var/log/myscript_$(date +%Y%m%d_%H%M%S).log"
{
echo "Starting script at $(date)"
echo "Running step 1..."
# step 1 commands here
echo "Running step 2..."
# step 2 commands here
echo "Script finished at $(date)"
} > "$LOG_FILE" 2>&1
echo "Log saved to $LOG_FILE"
Wrapping a block of commands in { ... } and redirecting the whole block at once is a clean way to capture everything without repeating the redirection on every single line.
Best Practices
- Use
>when you want a fresh file each run, and>>when you want to preserve history across runs, like an ongoing log. - Redirect stderr separately from stdout (
2> errors.log) when debugging, so you can quickly see what went wrong without digging through normal output. - Use
/dev/nullto discard output you genuinely don’t need, rather than leaving noisy commands cluttering your terminal or logs. - Prefer
&>or> file 2>&1(in that specific order) for combining streams, and remember that order matters. - Use
{ ... } > file 2>&1to redirect an entire block of commands at once instead of repeating redirection on every line.
Security Considerations
- Be cautious redirecting output to files in world-writable directories like
/tmp, especially in scripts running with elevated privileges, since predictable filenames can be exploited by other processes on multi-user systems. Prefermktempfor temporary log files. - Avoid logging sensitive data (passwords, API tokens, personal information) to files without appropriate permissions — set restrictive permissions (
chmod 600) on log files that might contain sensitive output. - When redirecting user-controlled input into a command, be mindful of what that command does with the data, especially if it involves any form of code execution.
Optimization Tips
- Redirecting an entire block of commands at once (
{ cmd1; cmd2; cmd3; } > file) is more efficient than opening and closing the file descriptor separately for each command. - For scripts that write a large volume of log output, consider buffering with a single redirected block rather than many small individual writes, which reduces the overhead of repeated file open/close operations.
Troubleshooting
Error messages still show up on screen even after redirecting: You likely redirected only stdout (>) and not stderr. Add 2>&1 or use &> to capture both.
2>&1 > file.txt doesn’t behave as expected: Order matters. Use > file.txt 2>&1 instead — redirect stdout to the file first, then point stderr at wherever stdout now goes.
A log file is empty even though the command produced output: Check if the program buffers its output differently when not connected to a terminal (common with some compiled programs). Tools like stdbuf or a program’s own flush/unbuffered flag can help in these cases.
Common Mistakes
- Assuming
>and2>&1in the wrong order will combine streams correctly — order matters for how the redirection is resolved. - Using
>when>>was intended, accidentally wiping out a log file that was meant to accumulate over time. - Forgetting that redirecting stdout doesn’t automatically redirect stderr, leaving error messages unexpectedly visible (or missing) depending on the goal.
- Not quoting filenames used in redirection when they might contain spaces or special characters.
FAQs
What’s the difference between > and >>? > overwrites the target file, truncating any existing content. >> appends to the end of the file, preserving what’s already there.
How do I redirect output to both a file and the terminal at the same time? Use tee, like command | tee output.txt, which writes to the file while still passing the output through.
What does 2>&1 actually mean? It redirects file descriptor 2 (stderr) to wherever file descriptor 1 (stdout) currently points, effectively merging the two streams.
How do I completely silence a command’s output? Redirect both streams to /dev/null: command > /dev/null 2>&1.
Summary
Redirection is the mechanism that gives you control over where a command’s input and output actually go, whether that’s a file, another command, or nowhere at all. The core operators — >, >>, <, 2>, and &> — cover the vast majority of everyday needs, while here documents, here strings, and custom file descriptors handle the more specialized cases. Once the distinction between stdout and stderr, and the order-sensitivity of combining them, becomes second nature, you’ll find redirection turning up useful in almost every script you write.
References
- Bash Reference Manual (Redirections): https://www.gnu.org/software/bash/manual/bash.html#Redirections
- GNU Bash Manual: https://www.gnu.org/software/bash/manual/bash.html
- Advanced Bash-Scripting Guide (I/O Redirection): https://tldp.org/LDP/abs/html/io-redirection.html