Bash redirection lets you send the output of a command to a file, or send the contents of a file as input to a command, all without needing to manually copy-paste.
Here’s a simple ls command, but instead of printing to your terminal, its output is captured and written to a file named listing.txt:
ls -l /home/user/documents > listing.txt
Now, if you want to see what’s inside listing.txt, you can use the cat command:
cat listing.txt
This will display the contents of listing.txt on your terminal, which is the same output you would have seen from the ls -l command if you hadn’t redirected it.
You can also use redirection to feed input to a command. For example, the sort command sorts lines of text. By default, it reads from standard input. You can make it read from a file using the < operator:
sort < listing.txt
This will sort the lines in listing.txt alphabetically and print the sorted output to your terminal.
Let’s consider a slightly more complex scenario. Suppose you have a script that performs some operations and you want to log all its output, both standard output (stdout) and standard error (stderr). You can redirect both to the same file.
Imagine a script named process_data.sh:
#!/bin/bash
echo "Starting data processing..."
# Simulate a successful operation
echo "Operation 1 successful."
# Simulate an error
ls /non_existent_directory > /dev/null 2>&1
echo "Operation 2 attempted."
echo "Data processing finished."
If you run this script directly, you’ll see the error message on your terminal:
./process_data.sh
Starting data processing...
Operation 1 successful.
ls: cannot access '/non_existent_directory': No such file or directory
Operation 2 attempted.
Data processing finished.
Now, let’s redirect both stdout and stderr to a log file named process.log:
./process_data.sh > process.log 2>&1
In this command:
>redirects standard output (file descriptor 1) toprocess.log.2>&1redirects standard error (file descriptor 2) to the same place as standard output. This is why the error message fromlsis now inprocess.logand not on your screen.
If you then cat process.log, you’ll see:
Starting data processing...
Operation 1 successful.
Operation 2 attempted.
Data processing finished.
Notice that the actual error message from ls is suppressed because it was redirected to /dev/null within the script. If the script didn’t redirect its own stderr, then 2>&1 would have captured it.
Here’s how you’d capture the error message itself:
./process_data.sh > process.log 2> process.err
Now, process.log contains:
Starting data processing...
Operation 1 successful.
Operation 2 attempted.
Data processing finished.
And process.err contains:
ls: cannot access '/non_existent_directory': No such file or directory
The >> operator is used for appending to a file instead of overwriting it. If you run the ls command again with >>:
ls -l /home/user/documents >> listing.txt
The new output of ls will be added to the end of listing.txt, preserving the original contents.
You can also pipe the output of one command directly into the input of another. This is called a pipe (|). For example, to see a sorted list of files in your current directory:
ls -l | sort
Here, the output of ls -l is sent directly as input to sort, without ever creating an intermediate file. This is often more efficient for sequential processing.
A powerful combination is using grep with pipes to filter output. If you wanted to find all lines in process.log that mention "successful":
cat process.log | grep "successful"
Or, more concisely:
grep "successful" process.log
Bash provides several file descriptors:
0: Standard Input (stdin) - where commands read data from.1: Standard Output (stdout) - where commands write their normal results.2: Standard Error (stderr) - where commands write error messages.
You can redirect any of these. For instance, to discard all standard error messages from a command:
some_command 2>/dev/null
This is very common when you want to run a command but don’t want to be bothered by its potential error output, like when installing packages that might print warnings you don’t care about.
The most surprising thing about redirection is how seamlessly it integrates with pipes to create complex data processing pipelines by chaining simple, single-purpose commands. You’re not just redirecting to files; you’re redirecting streams between processes.
Consider this: you want to find all lines in /var/log/syslog that contain the word "error" and count how many times it appears.
grep "error" /var/log/syslog | wc -l
This single line does a lot. grep reads /var/log/syslog, filters for lines containing "error", and then pipes those matching lines to wc -l (word count, lines). wc -l then counts those lines and prints the number. No temporary files, just a direct flow of data.
The mechanism behind 2>&1 is that file descriptors are numbers, and when you write 2>&1, you’re telling the shell to make file descriptor 2 (stderr) point to wherever file descriptor 1 (stdout) is currently pointing. If stdout is already redirected to a file, stderr will go to that same file. If stdout is still connected to the terminal, stderr will also go to the terminal.
The next thing you’ll want to explore is command substitution, which lets you use the output of a command as arguments to another command.