Output Redirection (>, >>)
Every command you run in Bash sends its output somewhere — normally straight to your terminal screen. Output redirection lets you capture that same output into a file instead, using the > and >> operators. This is one of the most fundamental tools in the shell: it’s how you save command results, build log files, generate reports, and pass data along without ever copying and pasting. Understanding exactly what > and >> do — and how they differ — will also save you from one of the most common and painful mistakes in Linux: accidentally erasing data you meant to keep.
Overview / How Output Redirection Works
When a program runs on Linux, the kernel gives it three open file descriptors by default: descriptor 0 is standard input (stdin), descriptor 1 is standard output (stdout), and descriptor 2 is standard error (stderr). A file descriptor is just a small integer that indexes into a table the kernel keeps for that process, pointing at an open file, pipe, or device. A program doesn’t know or care where descriptor 1 actually points — it just calls write() on it. By default, all three descriptors are connected to your terminal, which is why running ls prints to your screen.
Redirection changes what descriptor 1 points to before the program ever runs. When Bash sees command > file, it forks a child process, and in that child, before calling exec() to load the program, it opens file and uses the dup2() system call to make descriptor 1 point at that open file instead of the terminal. The program then runs completely unaware that anything changed — it just writes to descriptor 1 as always, and those bytes now land in the file.
The difference between > and >> comes down to how the file is opened:
>opens the file with theO_TRUNCflag (among others). If the file already exists, its contents are immediately discarded and its length is set to zero before the command writes a single byte. If it doesn’t exist, it’s created.>>opens the file with theO_APPENDflag instead. Existing content is left alone, and every write from the command lands at the current end of the file. The kernel guarantees each append happens atomically at the file’s end, which is what makes>>safe for multiple processes writing to the same log file.
Both forms create the file if it doesn’t already exist, and both require write permission on the target directory (to create the file) or on the file itself (to write to it). Neither one ever reads the file first — that distinction matters a lot, as you’ll see in Common Mistakes below.
By convention, > and >> with no number in front redirect descriptor 1 (stdout) only. Standard error (descriptor 2) is untouched and still prints to your terminal — redirecting stderr specifically, or combining both streams, uses 2> and 2>&1, which are covered in a dedicated lesson on error-stream redirection.
Syntax
command > file
command >> file
| Form | Meaning |
|---|---|
command > file |
Run command, send its standard output to file, overwriting file if it exists (creating it if not). |
command >> file |
Run command, append its standard output to the end of file (creating it if it doesn’t exist). |
command 1> file |
Identical to command > file — the 1 is the explicit, usually-omitted descriptor number for stdout. |
There must be no space between the two characters of >>; a space between the operator and the filename is optional and purely stylistic (>file and > file behave identically).
Examples
Example 1: Writing a file from scratch
echo "Deployment started" > deploy.log
cat deploy.log
Output:
Deployment started
deploy.log didn’t exist, so > created it and wrote the single line into it. cat then confirms exactly what’s in the file.
Example 2: How > overwrites on every run
echo "First line" > notes.txt
echo "Second line" > notes.txt
cat notes.txt
Output:
Second line
Notice “First line” is gone. The second > truncated notes.txt to zero bytes before writing “Second line”, exactly as if you’d deleted the file’s contents first. Each use of > on the same target wipes out whatever was there.
Example 3: Building a file with >>
echo "First line" > notes.txt
echo "Second line" >> notes.txt
cat notes.txt
Output:
First line
Second line
This time only the first command used >, to start the file fresh. The second used >>, so its output was added after the existing content instead of replacing it.
Example 4: A realistic logging script
#!/usr/bin/env bash
set -euo pipefail
LOG_FILE="/var/log/backup.log"
BACKUP_DIR="/backup"
SOURCE_DIR="/home/alice"
echo "Backup started at $(date)" >> "$LOG_FILE"
tar -czf "$BACKUP_DIR/home-$(date +%Y%m%d).tar.gz" "$SOURCE_DIR"
echo "Backup finished at $(date)" >> "$LOG_FILE"
This is the pattern almost every real logging script follows: >>, never >, so each run adds new entries instead of erasing the whole history. If this script had used > instead, every run would wipe out yesterday’s log before writing today’s.
Example 5: Saving a command’s output for later
ls -l /var/log > listing.txt
wc -l listing.txt
Output:
42 listing.txt
ls -l /var/log never printed to the screen at all — its output went straight into listing.txt. wc -l then reports how many lines that file contains, confirming the redirect worked without you needing to look at the raw listing.
How It Works Step by Step
Walking through ls -l /var/log > listing.txt from Example 5:
- Bash parses the line and recognizes
>as a redirection operator withlisting.txtas its target, separate from thels -l /var/logcommand itself. - Bash forks a child process to run
ls. - In the child, before
lsis loaded, Bash openslisting.txtwith the flags for “create if missing, truncate if it exists, write-only”, getting back a new file descriptor, say3. - Bash calls
dup2(3, 1), making descriptor 1 (stdout) point at the same open file as descriptor 3, then closes descriptor 3 since it’s no longer needed. - The child process calls
exec()to replace itself with thelsprogram. The file descriptor table survivesexec(), solsinherits a stdout that’s now connected tolisting.txtinstead of the terminal. lsruns normally, callingwrite()on descriptor 1 for every line of output, with no idea those bytes are landing in a file rather than being printed.- When
lsexits, the child process exits, the file is closed, and Bash’s parent shell resumes, moving on to runwc -l listing.txtas an entirely separate command.
The key insight is that redirection is a shell-level setup step, not something the command itself has to know about or support. This is why redirection works identically with every command on the system, from ls to a custom script — the operating system, not the program, does the routing.
Common Mistakes
Mistake 1: Using > when you meant >>
It’s easy to reach for > out of habit and accidentally erase a file you meant to add to:
echo "Day 1 entry" > activity.log
echo "Day 2 entry" > activity.log
cat activity.log
Output:
Day 2 entry
“Day 1 entry” is permanently gone — > gave no warning before truncating the file. If the goal was to build up a log over time, the second line should have used >>:
echo "Day 1 entry" > activity.log
echo "Day 2 entry" >> activity.log
cat activity.log
Mistake 2: Redirecting a command’s output back into its own input file
Because > truncates the target file before the command ever reads anything, redirecting into the same file you’re reading from destroys your data:
sort names.txt > names.txt
Bash opens and truncates names.txt as part of setting up the redirect, so by the time sort tries to read it, the file is already empty — sort ends up sorting nothing, and names.txt is left blank. The fix is to write to a different file, then move it into place once the command has finished:
sort names.txt > names.sorted.txt
mv names.sorted.txt names.txt
Mistake 3: Leaving a redirect target unquoted
An unquoted variable in a redirect target is subject to word splitting, just like anywhere else in Bash. A path with a space in it silently breaks:
REPORT_DIR=/home/alice/My Reports
echo "Status: OK" > $REPORT_DIR/status.txt
The unquoted $REPORT_DIR splits on the space, so Bash tries to redirect into a file literally named /home/alice/My and passes Reports/status.txt as if it were a separate word — not what was intended, and it fails outright since that directory doesn’t exist. Quoting both the assignment and the expansion fixes it:
REPORT_DIR="/home/alice/My Reports"
echo "Status: OK" > "$REPORT_DIR/status.txt"
Best Practices
- Default to
>>for logs and any file you expect to grow over multiple runs; reserve>for files you deliberately want to start fresh each time. - Always quote redirect targets that come from a variable or command substitution:
> "$file", not> $file. - Never redirect a command’s output back into one of its own input files with
>— write to a temporary file andmvit into place instead. - Discard output you don’t care about by redirecting to the special file
/dev/null, which silently accepts and discards any bytes written to it:
find /var/log -name "*.log" > /dev/null
- Make sure the target directory exists before redirecting into it (
mkdir -pthe parent first) — redirection can create the file, but never the directory path leading to it. - In scripts, check that a redirect actually succeeded when it matters (for example, that a log directory is writable) rather than assuming it silently worked; combine with
set -euo pipefailso a failed redirect stops the script instead of continuing silently. - Remember
>and>>only affect standard output (descriptor 1) — error messages from a failing command will still print to your terminal unless you separately redirect descriptor 2.
Practice Exercises
- Create a file called
colors.txtcontaining the single line “red”, then run a second command that adds the line “blue” underneath it without erasing “red”. Check the final file has both lines in order. - Write a short script named
disk-report.shthat appends the output ofdf -hto/tmp/disk-report.logevery time it runs, along with a timestamp line before it. Run it three times and confirm the log has three timestamped entries, not just one. - You have a file
readings.csvand want a sorted copy calledreadings-sorted.csvwithout touching the original. Write the command, then explain in one sentence why redirectingsort‘s output directly back intoreadings.csvwould have been a mistake.
Summary
>redirects a command’s standard output to a file, truncating (erasing) the file first if it already exists.>>redirects standard output to a file too, but appends to the end instead of erasing existing content.- Both operators create the target file if it doesn’t exist, and both act on descriptor 1 (stdout) only — not descriptor 2 (stderr).
- Redirection is set up by the shell using
open()anddup2()before the command runs; the command itself just writes to descriptor 1 as usual. - Using
>when you meant>>, or redirecting a command’s output back into its own input file, are two of the most common ways people accidentally lose data on the command line. - Always quote variables and command substitutions used as redirect targets to avoid word-splitting on spaces.
