Three doors every program has
Every Unix process is born with three open file descriptors: stdin (0), stdout (1), stderr (2). They are how the kernel and the shell talk to your program. The shell's pipes and redirections are nothing more than wiring up these three doors to different things.
Default wiring
- stdin (0) — your keyboard.
- stdout (1) — your terminal screen.
- stderr (2) — also your terminal screen, but a separate stream so errors don't mix into a pipe.
Why two output streams?
Imagine find / -name foo 2>/dev/null | grep bar. The errors ("Permission denied") go to stderr, which you silence with 2>/dev/null. The actual matches go through the pipe to grep. If find used a single stream, you couldn't separate the noise from the data. This split is the most useful design decision in Unix.
The numeric handles
You'll see numbers in redirections: 2> (stderr), 1> (stdout, default), 2>&1 (merge stderr into stdout). These are file descriptor numbers — direct kernel-level handles. You can open new ones too: exec 3> file opens fd 3, then echo hello >&3 writes to it.
A stream is a channel, not a content guarantee
stdin, stdout, and stderr are file descriptors carrying bytes; they do not promise text or an encoding. If the next command expects line-oriented UTF-8, verify the producer's contract. Feeding binary data into text tools can corrupt it silently.
Separating standard error improves composition
Put result data on stdout and progress or diagnostics on stderr. Callers can then save or pipe the result independently. Scripts and functions should keep the same boundary to remain useful pipeline components.