/dev/null and Discarding Output
/dev/null is a special file built into every Linux system that quietly throws away anything written to it. When a command produces output you don’t care about — a noisy status message, a warning you already know about, an entire log stream from a cron job — you redirect it to /dev/null instead of letting it clutter your terminal, fill a log file, or trigger an unwanted email from cron. Understanding exactly how it works, and how to target stdout and stderr independently, is essential for writing clean shell scripts.
Overview / How it works
/dev/null is a character device file, not a regular file. You can see this with ls -l /dev/null: it shows a c in the first column (character device) and a major/minor device number (1, 3 on Linux) instead of a file size. It exists on essentially every Unix-like system and is created automatically at boot (or by udev), so you never need to create it yourself.
Every process starts with three open file descriptors: 0 (stdin, input), 1 (stdout, normal output), and 2 (stderr, error output). The shell can rewire any of these to point at a different destination before running a command — this is what redirection operators like >, >>, and 2> do. When you redirect a descriptor to /dev/null, the kernel opens that device file and points the descriptor at it instead of the terminal.
The null device driver itself is trivially simple:
- Writes to
/dev/nullare accepted and immediately discarded. The driver’s write handler reports success (it returns the exact number of bytes it was given) without storing those bytes anywhere — no disk I/O happens, no inode is touched, and the “file” never grows. This is why/dev/nullalways shows a size of0no matter how much you write to it. - Reads from
/dev/nullreturn end-of-file (EOF) immediately — zero bytes, every time. This makes it useful as a source of “no input” for commands that expect to read from stdin but shouldn’t block waiting for a human to type something.
Because writing to /dev/null is nearly free (no disk access, just a kernel call that returns instantly), it’s also a cheap way to discard output when you only care about a command’s exit status, not what it printed.
Syntax
The general forms for sending a stream to /dev/null (or reading no input from it) are:
command > /dev/null # discard stdout only
command 2> /dev/null # discard stderr only
command > /dev/null 2>&1 # discard stdout AND stderr (order matters)
command &> /dev/null # Bash shorthand for the line above
command < /dev/null # feed empty input (immediate EOF) to a command
| Form | Meaning |
|---|---|
> /dev/null |
Redirect file descriptor 1 (stdout) to the null device; overwrites, doesn’t append. |
2> /dev/null |
Redirect file descriptor 2 (stderr) to the null device, leaving stdout on the terminal. |
2>&1 |
“Duplicate file descriptor 1’s current target onto file descriptor 2.” Must come after > /dev/null to have stderr follow stdout into the null device. |
&> /dev/null |
Bash-only shorthand that redirects both stdout and stderr to /dev/null in one step. |
< /dev/null |
Redirect stdin from the null device, so any read the command performs returns EOF instantly. |
Examples
Example 1: Discard stdout, keep stderr visible
ls /var/log/app.log /var/log/missing.log > /dev/null
Output:
ls: cannot access '/var/log/missing.log': No such file or directory
ls writes the successful listing (/var/log/app.log) to stdout, which we sent to /dev/null, so it never appears. The “No such file or directory” message goes to stderr, which was left pointing at the terminal, so it still prints. This pattern — hide the normal output, keep the errors — is the most common reason to use /dev/null.
Example 2: Discard stderr, keep stdout visible
ls /var/log/app.log /var/log/missing.log 2> /dev/null
Output:
/var/log/app.log
Now it’s reversed: the error about the missing file is thrown away, and only the successful result prints. This is handy when you expect some failures (say, checking several optional config paths) and only want to see what actually exists.
Example 3: Silence a command completely and check only its exit code
if curl -sf https://example.com/health > /dev/null 2>&1; then
echo "Service is up"
else
echo "Service check failed"
fi
Output:
Service is up
Here we don’t care about the response body or any curl progress/error text — only whether the request succeeded. Both streams go to /dev/null, and the if statement inspects curl’s exit status ($?, checked implicitly by if) to decide which branch runs. This is the standard shape of a health check or availability test in a script.
How it works step by step
For a command like backup.sh > /dev/null 2>&1, Bash performs these steps before your program ever runs:
- The shell forks a child process to run
backup.sh. - In that child, before
exec-ing the script, the shell processes redirections left to right.> /dev/nullopens/dev/nullfor writing and usesdup2()to make file descriptor 1 (stdout) point at it. 2>&1then duplicates whatever file descriptor 1 currently points to (the now-redirected/dev/null) onto file descriptor 2 (stderr).- The child process calls
exec()to becomebackup.sh, inheriting the two rewired descriptors. - Anything the script writes to stdout or stderr now lands in the null device’s write handler and is discarded; the script itself has no idea its output is going nowhere.
- When the script exits, its exit status is still reported normally — redirection only affects the data streams, never the exit code.
Common Mistakes
Mistake 1: Getting the redirection order backwards
backup.sh 2>&1 > /dev/null
This looks like it should silence everything, but it doesn’t. Redirections are applied left to right: 2>&1 first points stderr at whatever stdout currently is — the terminal — and then > /dev/null repoints stdout to the null device. Stderr is left attached to the terminal, so error messages still print. The fix is to send stdout to /dev/null first, then duplicate stderr onto it:
backup.sh > /dev/null 2>&1
Mistake 2: Using /dev/null to hide errors instead of fixing them
rm -f /tmp/app.lock 2> /dev/null
cp config.yaml /etc/myapp/config.yaml 2> /dev/null
Blanket-suppressing stderr on every command “just in case” is a common way to hide a real, silent failure (a typo’d path, a permissions problem) that then causes confusing behavior later. Only discard stderr when you’ve deliberately decided a specific failure is safe to ignore, and prefer checking the exit code explicitly so you still know something went wrong:
if ! cp config.yaml /etc/myapp/config.yaml; then
echo "Failed to install config" >&2
exit 1
fi
Mistake 3: Confusing > /dev/null with >> /dev/null
Some people reach for >> /dev/null out of habit, thinking append is “safer.” It makes no practical difference for /dev/null itself (nothing is ever stored either way), but it’s worth remembering that > truncates and >> appends for every other file — using > by habit on a real log file you meant to append to will silently wipe it.
Best Practices
- Redirect stdout and stderr separately (
> /dev/null, then2> /dev/null) when you want to keep visibility into errors while silencing normal output — don’t reach for&> /dev/nullby default. - When you do need to discard both streams, write
> /dev/null 2>&1(or the Bash shorthand&> /dev/null) and remember the ordering rule for the long form. - In cron jobs, redirect a command’s own output to a real log file instead of
/dev/nullwhen you might need to debug it later; only use/dev/nullfor output you’ve confirmed is genuinely noise. - Use
< /dev/nullfor commands launched in the background or oversshthat might otherwise try to read from a terminal and hang. - Still check
$?(or use the command directly in anif) after discarding output — silencing a stream never silences failure; you can and should keep checking whether the command succeeded. - Don’t try to “empty”
/dev/nullor change its permissions; it’s a shared system device maintained by the kernel/udev, not a regular file you manage.
Practice Exercises
- Write a command that lists both
/etc/passwdand a file that doesn’t exist, showing only the error message (hide the successful listing). - Write an
ifstatement that runsping -c 1 8.8.8.8, discards all of its output, and prints either"Network reachable"or"Network unreachable"based on the exit code. - Take the (broken) line
myscript.sh 2>&1 > /dev/nulland rewrite it so that both stdout and stderr are actually discarded. Explain in one sentence why the original order failed.
Summary
/dev/nullis a character device that discards everything written to it and returns EOF immediately when read — no disk I/O, no size, nothing recoverable.> /dev/nulldiscards stdout only;2> /dev/nulldiscards stderr only;> /dev/null 2>&1(or&> /dev/null) discards both.- Redirection order matters:
2>&1must come after stdout has already been pointed at/dev/null, not before. < /dev/nullsupplies immediate EOF as input, useful for background or non-interactive commands.- Discarding output never discards the exit status — keep checking
$?or using the command in a conditional so failures aren’t silently lost along with the text. - Use
/dev/nulldeliberately for known noise, not as a blanket way to hide errors you haven’t investigated.
