Definition
Every Unix process starts life with three file descriptors already open: file descriptor 0 is standard input, file descriptor 1 is standard output, and file descriptor 2 is standard error. By default these are wired to the controlling terminal — stdin to the keyboard, stdout and stderr both to the screen — but the parent process can redirect any of them to a file, a pipe, or another open descriptor before the new program starts running.
The convention is small but precise. Standard input carries the data the program operates on. Standard output carries the program's primary result, in a form intended to be consumed by another program. Standard error carries diagnostic messages intended for a human reading the terminal. Separating data from diagnostics is what allows the output of one program to feed the input of another without log lines polluting the stream.
Why it matters
How it works
When the kernel forks a new process from its parent, the child inherits a copy of the parent's open file-descriptor table. The shell exploits this by opening or duplicating descriptors in the brief window between fork and exec: an input redirection opens the target file as descriptor 0, an output redirection opens or creates the target file as descriptor 1, and a pipe uses the dup2 system call to point descriptor 1 of one process at the write end of a kernel pipe whose read end becomes descriptor 0 of the next process. By the time the new program starts running, its three standard streams already point wherever the shell decided.
The program itself does not see this plumbing. It simply reads from descriptor 0 and writes to descriptors 1 and 2 using the ordinary file-I/O calls. This is what makes the abstraction so durable: a tool written in 1975 that reads stdin and writes stdout still composes cleanly with a tool written today, because both speak the same byte-stream convention. The price of the abstraction is that the streams are unstructured — just bytes — so when programs need richer formats they layer JSON, TSV, or a domain protocol on top of the byte stream rather than replacing it.