Table of Contents#
- Understanding Standard Streams
- Basic Output Redirection in Bash
- Combining stdout and stderr
- Why Output Still Appears in the Terminal?
- Fixing Persistent Terminal Output: Advanced Solutions
- Practical Examples
- Summary
- References
Understanding Standard Streams#
Before diving into redirection, it’s critical to understand how Linux handles input and output. All programs interact with the system through standard streams—predefined channels for input, output, and errors.
What Are Standard Streams?#
Every process in Linux has three default streams:
- Standard Input (stdin): Data input to the program (e.g., keyboard input).
- Standard Output (stdout): Normal program output (e.g., results of
ls). - Standard Error (stderr): Error messages (e.g., "file not found").
File Descriptors: stdin, stdout, stderr#
Streams are identified by file descriptors (FDs)—numeric handles the kernel uses to track open files. By default:
0= stdin (input)1= stdout (output)2= stderr (errors)
Most programs write regular output to stdout (FD 1) and errors to stderr (FD 2). Redirection lets you reroute these streams to files instead of the terminal.
Basic Output Redirection in Bash#
Bash provides operators to redirect stdout, stderr, or both to files. Let’s start with the basics.
Redirecting stdout to a File#
Use the > operator to redirect stdout (FD 1) to a file. This overwrites the file if it exists; use >> to append instead.
Syntax:
command > output_file.txt # Overwrite file with stdout
command >> output_file.txt # Append stdout to file Example:
Save the output of ls -l to directory.log:
ls -l > directory.log Now, directory.log contains the directory listing, and no output appears in the terminal.
Redirecting stderr to a File#
Errors (stderr, FD 2) are not redirected by >. Use 2> to redirect stderr explicitly.
Syntax:
command 2> error_file.txt # Overwrite file with stderr
command 2>> error_file.txt # Append stderr to file Example:
Redirect errors from a failed ls command to errors.log:
ls -l non_existent_file 2> errors.log The error message (ls: cannot access 'non_existent_file': No such file or directory) is saved to errors.log instead of the terminal.
Combining stdout and stderr#
To capture both normal output and errors in a single file, merge stderr into stdout.
Merging stderr into stdout (2>&1)#
The 2>&1 syntax redirects stderr (FD 2) to the same destination as stdout (FD 1).
Syntax:
command > combined.log 2>&1 # Overwrite: stdout + stderr to combined.log
command >> combined.log 2>&1 # Append: stdout + stderr to combined.log Example:
Capture both output and errors from curl (e.g., downloading a file):
curl https://example.com/file.zip > download.log 2>&1 Bash-Specific Shortcut: &> and &>>#
Bash (and Zsh) offer a shorter syntax for merging streams: &> (overwrite) or &>> (append).
Syntax:
command &> combined.log # Equivalent to > combined.log 2>&1
command &>> combined.log # Equivalent to >> combined.log 2>&1 Note: &> is not POSIX-compliant (e.g., won’t work in sh). Use 2>&1 for portability.
Why Output Still Appears in the Terminal?#
Even after redirecting stdout and stderr, you may notice output leaking to the terminal. Common causes include:
Writing Directly to /dev/tty#
Some programs bypass stdout/stderr and write directly to the terminal via /dev/tty—a special file representing the current terminal. Examples include:
- Password prompts (e.g.,
sudo,ssh). - Interactive tools (e.g.,
nano,top).
/dev/tty is not affected by stdout/stderr redirection, so output here always goes to the terminal.
Background Processes and Subshells#
Redirection may fail if applied to a subshell or background process. For example:
# Subshell with unredirected output
(echo "Hello from subshell") > main.log # Works (output to main.log)
# Background process with missing redirection
echo "Background task" & > background.log # Fails: & runs in background; redirection is ignored Interactive Commands and Prompts#
Interactive programs (e.g., apt-get, passwd) often prompt for user input and write prompts directly to the terminal, even with redirection.
Fixing Persistent Terminal Output: Advanced Solutions#
To capture output that evades basic redirection, use these tools and techniques.
Using script to Capture Terminal Sessions#
The script command records all terminal input/output, including output written to /dev/tty. It creates a log of the entire session, making it ideal for capturing interactive or stubborn commands.
Syntax:
script [options] logfile # Start a session; log to logfile
script -c "command" logfile # Run a single command and log output Example: Capture sudo output
sudo writes password prompts to /dev/tty, so basic redirection fails:
# Fails: Prompt still appears in terminal
sudo ls > sudo.log 2>&1 Use script to capture it:
# Capture sudo prompt and output
script -c "sudo ls" sudo_session.log Enter your password when prompted. sudo_session.log now contains the prompt, command output, and errors.
Debugging with strace to Identify Output Sources#
If you’re unsure where a program writes output, use strace to trace system calls. It shows which file descriptors (e.g., stdout, stderr, /dev/tty) the program uses.
Syntax:
strace -e write command # Trace write() system calls Example: Check where sudo writes
strace -e write sudo ls 2>&1 | grep -i write Look for lines like write(3, "Password: ", 10)—here, FD 3 might point to /dev/tty.
Practical Examples#
Let’s apply these concepts to real-world scenarios.
Example 1: Basic Redirection of stdout and stderr#
Goal: Save ping output and errors to a log file.
# Ping example.com 4 times; redirect all output to ping.log
ping -c 4 example.com &> ping.log
# Verify: No output in terminal; check log
cat ping.log Example 2: Capturing a Stubborn sudo Prompt#
Goal: Log sudo apt update (including the password prompt).
# Use script to capture the entire sudo session
script -c "sudo apt update" apt_update.log
# Enter password when prompted. Log contains:
# - Password prompt
# - Update output
# - Errors (if any) Example 3: Logging an Interactive Script#
Goal: Log an interactive Python script that writes to /dev/tty.
Step 1: Create a script interactive_script.py:
import sys
print("This goes to stdout") # stdout (FD 1)
sys.stderr.write("This goes to stderr\n") # stderr (FD 2)
with open("/dev/tty", "w") as tty:
tty.write("This goes to /dev/tty\n") # Bypasses redirection Step 2: Basic redirection fails to capture /dev/tty output:
python3 interactive_script.py > script.log 2>&1
# "This goes to /dev/tty" still appears in terminal Step 3: Use script to capture all output:
script -c "python3 interactive_script.py" script_session.log
# All output (stdout, stderr, /dev/tty) is saved to script_session.log Summary#
- Standard Streams: Use
stdout(FD 1) for output,stderr(FD 2) for errors. - Basic Redirection:
> file(stdout),2> file(stderr),>>to append. - Merge Streams: Use
2>&1(POSIX) or&>(Bash) to combinestdoutandstderr. - Fix Stubborn Output: Use
scriptto capture terminal sessions (including/dev/ttywrites). - Debug: Use
straceto trace where a program writes output.