| Tested on | RHEL 10.2 (Coughlan) |
|---|---|
| Package | bash 5.2.26-6.el10 |
| Applies to | Linux distributions with Bash installed |
| Privilege | Normal user; sudo -u nobody used for permission-denied examples |
| Scope | Bash redirection for FD 0/1/2: <, >, >>, 2>, 2>>, >&2, 2>&1, &>/&>> (Bash), split destinations, /dev/null, brace groups, and exec script-wide redirects. Does not cover named/custom file descriptors, pipelines and pipefail, heredocs, or full logging architecture. |
| Related guides | Bash command substitution Bash shell expansions Shell scripting course hub |
Bash redirection connects a command's standard input, output, and error streams to files, devices, or other file descriptors. The operators look small, but Bash applies them left to right while updating descriptor state — so cmd >out.log 2>&1 and cmd 2>&1 >out.log are not the same command.
This guide uses one helper that prints a tagged stdout line and a tagged stderr line. Every example shows exactly what lands in the terminal and what lands in each file.
What Are stdin, stdout and stderr?
Commands normally inherit three standard file descriptors from the shell:
| FD | Name | Typical interactive connection | Typical use |
|---|---|---|---|
| 0 | stdin | Terminal | Command reads input |
| 1 | stdout | Terminal | Normal program output |
| 2 | stderr | Terminal | Errors and diagnostics |
In a script or pipeline, stdin may come from a file, pipe, or socket instead of the terminal. Redirection changes where FD 0, 1, or 2 point before the command runs. Bash does not move bytes after the fact — it rewires the descriptors, then executes the command.
The helper used in this article writes one line to each stream:
demo_io() {
printf 'DATA: stdout line\n'
printf 'DIAG: stderr line\n' >&2
}Run it with no redirection to see the baseline — stdout and stderr both appear on the terminal:
demo_ioDATA: stdout line
DIAG: stderr lineDATA: marks normal output; DIAG: marks diagnostics. The tags make it obvious which stream each line used.
Bash Redirection Quick Reference
| Operator | Effect |
|---|---|
< file / 0< file |
Read stdin from file |
> file / 1> file |
Redirect stdout to file (truncate) |
>> file / 1>> file |
Redirect stdout to file (append) |
2> file |
Redirect stderr to file (truncate) |
2>> file |
Redirect stderr to file (append) |
>&2 / 1>&2 |
Send stdout to the current stderr destination |
2>&1 |
Duplicate FD 2 from whatever FD 1 points to right now |
&> file / &>> file |
Bash: redirect stdout and stderr together (not POSIX sh) |
>out 2>err |
Send stdout and stderr to different files |
When the file descriptor is omitted before >, Bash uses FD 1 (stdout), so >file and 1>file are equivalent. The same rule applies to < and FD 0.
Bash processes redirections in source order. That order is the key to understanding 2>&1.
Redirect stdout with > and >>
> sends FD 1 to a file and truncates it first. >> appends instead.
Save only stdout while stderr still prints on the terminal:
demo_io >stdout.logDIAG: stderr lineStdout went to the file; stderr was never redirected:
cat stdout.logDATA: stdout line>> keeps prior file contents. Truncate the file first so the append demo starts from a known state — >> itself does not clear an existing file:
: >append.log
demo_io >>append.log
demo_io >>append.logDIAG: stderr line
DIAG: stderr lineOnly the stdout lines were appended:
cat append.logDATA: stdout line
DATA: stdout lineRedirect stdin with <
< points FD 0 at a file. Use it when a command already reads stdin — there is no need to pipe from cat when the file can feed the loop directly:
printf 'input line\n' > data.txt
while IFS= read -r line; do printf 'read=%s\n' "$line"; done < data.txtread=input lineThe same pattern appears in read a file line by line with while IFS= read -r loops.
Redirect stderr with 2> and 2>>
2> sends only FD 2 to a file. FD 1 stays on the terminal unless you redirect it separately.
Capture diagnostics while normal output still prints:
demo_io 2>stderr.logDATA: stdout lineCheck what landed in the stderr file:
cat stderr.logDIAG: stderr line2>> appends stderr the same way >> appends stdout. Start from an empty file again:
: >diag.log
demo_io 2>>diag.log
demo_io 2>>diag.log
cat diag.logDIAG: stderr line
DIAG: stderr lineSend stdout to stderr with >&2
>&2 is shorthand for 1>&2: make stdout (FD 1) write to the same destination as stderr (FD 2). Scripts use this when normal output should stay on stdout and warnings belong on stderr:
printf 'Error: configuration missing\n' >&2Error: configuration missingThat line appears on stderr (the terminal in an interactive shell), not in a redirected stdout file. The helper's printf 'DIAG: …\n' >&2 uses the same operator.
What Does 2>&1 Mean?
2>&1 makes FD 2 a duplicate of FD 1's current destination. It does not create a permanent link between them — later changes to FD 1 do not automatically move FD 2.
Before any redirection on an interactive terminal:
1 → terminal
2 → terminalAfter >out.log:
1 → out.log
2 → terminalAfter 2>&1 (with FD 1 already pointing at out.log):
1 → out.log
2 → out.logIf 2>&1 runs before >out.log, FD 1 still points at the terminal when the duplication happens. FD 2 is copied from that terminal connection. A later >out.log rewires only FD 1 — FD 2 keeps writing to the terminal.
That is why order matters.
Why Redirection Order Matters
Compare the two orderings side by side.
Both stdout and stderr go to the file:
demo_io >combined.log 2>&1
cat combined.logDATA: stdout line
DIAG: stderr lineStep by step:
>combined.logconnects FD 1 tocombined.log.2>&1duplicates FD 2 from FD 1, so stderr also writes tocombined.log.
Only stdout goes to the file:
demo_io 2>&1 >stdout-only.logDIAG: stderr lineStdout reached the file; stderr stayed on the terminal:
cat stdout-only.logDATA: stdout lineHere 2>&1 ran while FD 1 still pointed at the terminal, so FD 2 was duplicated from the terminal. The later >stdout-only.log moved only FD 1 to the file.
When you need one log file, use >file 2>&1 (or &>file in Bash). When you need stderr on the terminal and stdout in a file, use >file alone.
Redirect stdout and stderr to One File
>>file 2>&1 appends both streams to the same file:
: >combined_append.log
demo_io >>combined_append.log 2>&1
demo_io >>combined_append.log 2>&1
cat combined_append.logDATA: stdout line
DIAG: stderr line
DATA: stdout line
DIAG: stderr lineBash also provides &> and &>> as shorthand for >file 2>&1 and >>file 2>&1:
demo_io &>amp.log
cat amp.logDATA: stdout line
DIAG: stderr lineAppend both streams on repeated runs:
: >amp_append.log
demo_io &>>amp_append.log
demo_io &>>amp_append.log
cat amp_append.logDATA: stdout line
DIAG: stderr line
DATA: stdout line
DIAG: stderr line&> is a Bash extension. For POSIX sh scripts, use >file 2>&1 instead.
Send stdout and stderr to Different Files
Use separate redirection operators when data and diagnostics should land in different logs:
demo_io >split.out 2>split.err
cat split.outDATA: stdout lineThe diagnostics file holds stderr only:
cat split.errDIAG: stderr lineUse >file 2>&1 when a single combined log is enough. Use >data 2>diag when downstream tools consume stdout and humans read stderr separately.
Discard Output with /dev/null
/dev/null accepts and discards any amount of data. Redirect a stream there when you want silence:
demo_io >/dev/null 2>/dev/null
echo 'silent success'silent successDiscard only stderr while keeping stdout:
demo_io 2>/dev/nullDATA: stdout lineNormal > redirection sends a stream to the file instead of the terminal. When you need to save output and still see it on the screen, use tee through a pipeline — for example command | tee output.log, or command 2>&1 | tee output.log to include stderr. Pipeline exit-status handling is covered in redirection and pipes.
Redirect a Whole Command Group or Function
Curly braces group commands so one redirection applies to every command in the block. In a brace group, { and } must be separate shell tokens, and the command list must end with a semicolon or newline before }:
{ demo_io; printf 'DATA: second stdout\n'; } >group.log 2>&1
cat group.logDATA: stdout line
DIAG: stderr line
DATA: second stdoutThe same syntax works on a function call:
redirect_demo() {
demo_io
printf 'DATA: fn tail\n'
}
redirect_demo >fn.log 2>&1
cat fn.logDATA: stdout line
DIAG: stderr line
DATA: fn tailRedirect the Rest of a Script with exec
When exec is used with redirections but no command, the redirections are applied to the current shell. Commands run afterward inherit those changed file descriptors.
Run exec inside a throwaway subshell so you do not redirect your interactive session:
bash -c 'exec >script.log 2>&1; demo_io() { printf "DATA: stdout line\n"; printf "DIAG: stderr line\n" >&2; }; demo_io; printf "after exec\n"'
cat script.logDATA: stdout line
DIAG: stderr line
after execexec >file 2>&1 is the usual pattern for script-wide logging. Open the log once at the top; individual commands inside the script do not need their own > operators.
Why sudo command > file Can Still Fail
Redirection is parsed by the current shell before the command on the line runs. When you type:
sudo printf 'root log\n' > /root/app.logyour shell — not sudo — tries to open /root/app.log for writing. If your user cannot create that file, the redirect fails before sudo starts.
Simulate that as an unprivileged user:
sudo -u nobody bash -c 'printf "root log\n" > /root/app.log' 2>&1bash: line 1: /root/app.log: Permission deniedprintf never ran. The shell could not open the target file, so Bash reported the redirect error and exited with a non-zero status.
Put the redirect inside the elevated command instead:
sudo bash -c 'printf "root log\n" > /root/app.log'
sudo cat /root/app.logroot logA normal user usually cannot read /root directly, so use sudo for the verification step too.
Now the shell that sudo starts performs the redirection with root privileges. See the sudo command guide for elevation patterns; the redirect must live in the shell that has permission to open the file.
Common Redirection Mistakes
| Symptom / trap | Likely cause | Fix |
|---|---|---|
| stderr missing from a "combined" log | 2>&1 before >file |
Use >file 2>&1 or &>file |
Permission denied with sudo cmd > /root/file |
Current shell opens the file | sudo bash -c 'cmd > /root/file' |
2>file behaves differently from 2 >file |
With a space, 2 becomes a command argument and > redirects stdout |
Keep the FD attached to the operator: 2>file |
Output corrupted when using >file 2>file |
The same file is opened separately for FD 1 and FD 2 | Use >file 2>&1 |
| Command never runs, redirect error only | Shell cannot open target file | Fix path, permissions, or parent directory |
&> fails under sh |
Bash-only operator | Use >file 2>&1 for POSIX shells |
| Empty file after redirect | Command wrote only to stderr | Add 2>&1 or redirect stderr separately |
{ cmd parse error |
{ not a separate token or missing ; before } |
{ cmd; } — {/} separated from words; list ends with ; or newline before } |
A failed redirection can prevent the command from running at all. Bash opens redirection targets before executing the command; if opening fails, you see the shell error and the command does not start.
Summary
Bash redirection rewires file descriptors 0, 1, and 2 before a command runs. Use > and >> for stdout, 2> and 2>> for stderr, >&2 to send stdout to stderr, and < to feed stdin from a file. The operator 2>&1 duplicates FD 2 from FD 1 at the moment it is parsed — so cmd >file 2>&1 sends both streams to file, while cmd 2>&1 >file leaves stderr on the terminal.
Bash &> and &>> combine both streams in one step. /dev/null discards output you do not need. Use tee when output must appear on the terminal and in a file at the same time. Brace groups and functions accept a single redirection for the whole block, and exec >log 2>&1 redirects the rest of a script.
Remember that your shell performs redirection before sudo runs. Put > /root/file inside the elevated bash -c '…' when root must own the open file.
References
Frequently Asked Questions
What does 2>&1 mean?
It makes FD 2 a duplicate of FD 1's current destination at that point in the command line. It does not automatically mean "stderr goes to the stdout file" unless FD 1 already points there.
Why does 2>&1 >file behave differently from >file 2>&1?
Bash applies redirections left to right. In 2>&1 >file, stderr is duplicated from the terminal-bound FD 1 before stdout is moved to file. In >file 2>&1, stdout goes to file first, then stderr is duplicated from that connection.
How do I redirect stderr only?
Use 2>file or 2>>file. FD 1 stays on the terminal unless you redirect it separately with > or >>.
How do I append both stdout and stderr?
Use >>file 2>&1 or Bash &>>file. The append operator must open the file before 2>&1 duplicates FD 2 from FD 1.
Why can redirection fail even with sudo?
The current shell opens redirection targets before the command runs. In sudo cmd > /root/file, your shell tries to create /root/file — not sudo. Put the redirect inside the elevated shell: sudo bash -c 'cmd > /root/file 2>&1'.

