Stdin, Stdout, and Stderr: Linux I/O Streams Explained
Table of Contents
Programs running in a shell have a standard way of talking to you and to each other: three data streams called stdin, stdout, and stderr. Combine those streams with flags, positional arguments, and exit codes, and you can chain simple commands together into incredibly powerful automations using redirection and pipes.
In this post we'll cover how programs accept input (flags, arguments, and stdin), how they produce output (stdout, stderr, and exit codes), and how to glue it all together with the pipe operator.
All the content from our Boot.dev courses are available for free here on the blog. This one is the "Input/Output" chapter of Learn Linux. If you want to try the far more immersive version of the course, do check it out!
Getting Help With a Command
By convention, most production-ready CLI tools have a "help" option that prints information about how to use the tool. It's usually accessed with one of the following:
--help(flag)-h(flag)help(first positional argument)
The "help" output is often easier to parse than a full man page. It's usually more of a quick start guide than a full manual.
For example:
grep --help
If you're ever stuck wondering how a command works, this should be your first move.
What Are Command Line Flags?
Flags are options that you can pass to a command to change its behavior.
For example, the ls command can take a -l flag to show a "long" listing of files:
ls -l
Or the -a flag to show "all" files, including hidden files:
ls -a
You can also combine flags:
ls -al
Whether or not a command takes flags, and what those flags are, is up to the developer of the command. That said, there are some common conventions:
- Single-character flags are prefixed with a single dash (e.g.
-a) - Multi-character flags are prefixed with two dashes (e.g.
--help) - Sometimes the same flag can be used with a single dash or two dashes (e.g.
-hor--help)
For example, the "word count" command, wc, takes a -c flag (think "byte count") to count the number of bytes in a file:
wc -c pr_ideas.txt
# 4136 pr_ideas.txt
What Are Positional Arguments?
Programming languages have functions, and functions take arguments. For example, this Python function takes two parameters, xp and level:
def print_player(xp, level):
print("Player has", xp, "xp and is level", level)
It can then be called with two arguments:
print_player(100, 2)
# Player has 100 xp and is level 2
In a shell, commands (programs) can also take arguments. For example, the cd command takes a single argument (the directory to change to):
cd /home/wagslane
Other commands might take multiple arguments. For example, the mv command takes two arguments: the file to move, and the destination to move it to:
mv file.txt dest/file.txt
Unlike flags, positional arguments don't have names — the program knows what each one means based purely on its position in the command.
What Are Exit Codes in Linux?
Exit codes (sometimes called "return codes" or "status codes") are how programs communicate back whether they ran successfully or not.
0 is the exit code for success. Any other exit code is an error. 9 times out of 10, if a non-zero exit code is returned (meaning an error) it will be 1, which is the "catch-all" error code.
Programs that call other programs use error codes to figure out if execution was successful. For example, if the Boot.dev server program exits with a non-zero exit code, we have another program that will automatically restart it and log the error.
In a shell, you can access the exit code of the last program you ran with the question mark variable ($?):
cat greeting.txt
# General Kenobi!
echo $?
# 0
cat file/that/does/not/exist.txt
echo $?
# 1
You can run multiple commands on a single line by separating them with a semicolon (;):
command1 ; command2
If you only want the second command to run when the first command succeeds (exit code 0), use &&:
command1 && command2
What Is Stdout?
You might not even know it yet, but you're already a pro at using standard output. If you've ever run a command in a terminal, you've used it.
"Standard Output", usually called "standard out" or stdout, is the default place where programs print their output. It's just a stream of data that prints to your terminal, but it can be redirected to other places (more on that in a minute).
All programming languages have a simple way to print to stdout. In Python, it's the print function:
print("Hello world")
# Hello world
In a shell, it's the echo command:
echo "Hello world"
# Hello world
What Is Stderr?
Standard Error, usually called stderr, is a data stream just like standard output, but is intended to be used for error messages.
It's a separate stream so that you can redirect it to a different place if need be, but by default, it prints to your terminal just like stdout.
How Do You Redirect Output to a File?
You can redirect stdout and stderr to different places using the > and 2> operators. > redirects stdout, and 2> redirects stderr.
To redirect stdout to a file:
echo "Hello world" > hello.txt
cat hello.txt
# Hello world
To redirect stderr to a file:
cat doesnotexist.txt 2> error.txt
cat error.txt
# cat: doesnotexist.txt: No such file or directory
In this example, cat is used to intentionally generate an error message (since the file doesn't exist), which is then redirected to error.txt.
One warning: > truncates (overwrites) the destination file. If you want to append to the end of an existing file instead, use >>.
A common trick is to redirect noisy error output to the temporary directory:
./process_transactions.sh 2> /tmp/transactions.log
The /tmp directory exists by default on all Unix-like systems, in your root directory. Files there are deleted by your system routinely, so it's a great place to store temporary files that you don't need to keep around.
What Is Stdin?
If there's a standard output, there must be a standard input, right?
"Standard Input", usually called "standard in" or stdin, is the default place where programs read their input. It's just a stream of data that programs can read from as they run.
All major programming languages provide a simple way to read from stdin. In Python, it's the input function:
name = input("What is your name? ")
print("Hello,", name)
# Hello, Lane
The < operator redirects a file into a program's stdin. For example, to feed the contents of a file called input.txt into the "word count" program, you can run:
wc < input.txt
This is not the same as wc input.txt! In wc input.txt, the wc program is accepting a filepath string as an argument, and it opens the file itself. In wc < input.txt, the wc program doesn't know anything about the file — the file's contents are just being sent to the program's stdin, and it reads from there.
How Do Pipes Work in Linux?
One of the most beautiful things about the shell is that you can pipe the output of one program into the input of another program. With this one simple concept, you can run incredibly powerful automation tasks.
The pipe operator is |. It's the character that looks like a vertical line. It's usually on the same key as the backslash (\) above the Enter key. The pipe operator takes the stdout of the program on the left and "pipes" it into the stdin of the program on the right.
echo "Have you heard the tragedy of Darth Plagueis the Wise?" | wc -w
# 10
In the example above, the echo command sends "Have you heard the tragedy of Darth Plagueis the Wise?" to stdout.
However, instead of that text being sent to your terminal, the pipe operator pipes it into the wc (word count) command. The -w flag tells wc to only count words.
This only works because the wc command, like most shell commands, can optionally read from stdin instead of a filepath argument.
Pipes shine when you combine search tools with counting tools. Say you want to know how many transactions in a directory mention "Bob":
grep -r "Bob" transactions | wc -l
# 3973
grep finds every matching line, and wc -l counts them. Two simple programs, one powerful result.
The Unix Philosophy
The Unix Philosophy is a simple set of principles that have guided the development of Unix-like operating systems for decades. It can be summarized as:
- Write programs that do one thing and do it well.
- Write programs to work together.
- Write programs to handle text streams, because that is a universal interface.
1. Write programs that do one thing and do it well
This is why programs like ls, grep, and less exist. They do one thing, and they do it well. They don't try to do too much.
lslists files and directoriesgrepsearches for textlessdisplays text
2. Write programs to work together
Because, at least according to the Unix Philosophy, programs should do one thing and do it well, it's easy to write programs that work together. For example, you can use grep to search for text in a file, and then pipe the output of grep into less to display the results interactively:
grep "hello" some_file.txt | less
3. Write programs to handle text streams, because that is a universal interface
This point is more the "how" of the previous point. Programs work together easily when they all use the same interface: text streams. A text stream is just a sequence of characters that can be read or written sequentially. In other words, a text stream is just text.
The shell is a command-line (text) interface, and text-based interfaces are much more powerful and extensible than graphical interfaces. That's why developers have been using them for decades, and why what we can do with them looks like magic to the uninitiated.
If you want to keep building on these fundamentals, the natural next steps are customizing your own shell configuration and understanding file permissions — or work through the whole Learn Linux course from the start.
Frequently Asked Questions
What are the file descriptor numbers for stdin, stdout, and stderr?
Stdin is file descriptor 0, stdout is 1, and stderr is 2. That's why the 2> operator redirects stderr: the 2 refers to stderr's file descriptor number.
How do I redirect stderr to stdout?
Use 2>&1, which sends stderr to wherever stdout is currently pointing. For example, command > output.txt 2>&1 writes both stdout and stderr to output.txt.
What is the difference between > and >> in Linux?
The > operator overwrites the destination file with the command's output, while >> appends the output to the end of the file without deleting what's already there.
What does a non-zero exit code mean?
An exit code of 0 means the program succeeded, and any non-zero exit code means it failed. Most of the time a failing program returns 1, the catch-all error code.
What is the difference between a pipe and a redirect?
A redirect (>, 2>, or <) connects a program's stream to a file, while a pipe (|) connects the stdout of one program directly to the stdin of another program.
