LearnLinux FoundationsI/O Redirection

File Descriptors: The Three Channels

bash - a hands-on Linux lab on a real virtual machine.

Every command has three numbered channels: stdin (0), stdout (1), stderr (2). Watch the wiring in a live flow diagram, prove the two output channels are separate by isolating each one, then do it again from memory in two challenges.

You have a folder called signal in your home directory. Inside it are a few small text files and one little program, report.sh, that a monitoring team runs to check on a machine. When they run it, it says two things that went right and one thing that went wrong, all at once, jumbled together on the screen.

Here is the puzzle this whole module solves. A Linux command does not have one output. It has two: a channel for normal results, and a separate channel for error messages. On the screen they look mixed together, but underneath they are two distinct rivers. Once you can tell them apart, you can send each one wherever you want: the normal results into a file, the errors into a log, or either one straight into the next command.

This first lesson hands you the map: the three numbered channels every command carries. You will prove the two output channels are really separate by showing each one completely alone, then do it again from memory in two challenges. Every other lesson in this module builds on these three numbers.

The black boxes below are a practice terminal: a safe sandbox that only checks the one command each step teaches, so you cannot break anything. On the real machine at the end of the module, the mission builds this same signal folder and you run these moves for real.

Every running command is handed three channels, and Linux numbers them. They are called file descriptors, which is just Linux's name for a numbered connection to a stream of data:

That last point is the key. Normal output rides channel 1; error output rides channel 2. They both land on the screen unless you say otherwise, so they appear tangled. The number is the handle you grab when you want to pull one stream apart from the other.

Watch the default wiring. Data flows in on channel 0, and both output channels pour onto the same screen:

{ "caption": "Every command is born wired like this: channel 0 reads from the keyboard, and channels 1 and 2 both point at the screen by default, which is why results and errors look mixed.", "nodes": [ { "id": "kbd", "label": "keyboard", "kind": "stdin", "col": 0, "row": 1 }, { "id": "cmd", "label": "report.sh", "kind": "command", "col": 2, "row": 1 }, { "id": "scr1", "label": "screen", "kind": "screen", "col": 4, "row": 0 }, { "id": "scr2", "label": "screen (same one)", "kind": "screen", "col": 4, "row": 2 } ], "edges": [ { "from": "kbd", "to": "cmd", "label": "stdin (0)", "kind": "stdin", "stage": 1 }, { "from": "cmd", "to": "scr1", "label": "stdout (1)", "kind": "stdout", "stage": 2 }, { "from": "cmd", "to": "scr2", "label": "stderr (2)", "kind": "stderr", "stage": 3 } ] }

Errors did not always have their own channel. stderr was added to Unix in the mid 1970s, after Bell Labs programmers pointed program output at an expensive phototypesetter and watched error messages get typeset into the middle of their documents. The fix was a second output channel, and it has been number 2 ever since.

Time to prove the two output channels are really separate. report.sh writes its two normal lines to channel 1 and its one error line to channel 2. If you tell the shell to throw channel 2 away, only the normal output can survive.

The tool for that is 2>, which means "take channel 2 and send it somewhere else." You will aim it at a special bin called /dev/null that silently swallows anything sent to it. Both 2> and /dev/null get full lessons of their own later in this module; for now, 2> is the grab and /dev/null is the bin. Here is what that does to the wiring:

{ "caption": "bash report.sh 2>/dev/null: channel 1 keeps its default path to the screen, while channel 2 is re-pointed into the discard bin.", "nodes": [ { "id": "cmd", "label": "report.sh", "kind": "command", "col": 0, "row": 1 }, { "id": "scr", "label": "screen", "kind": "screen", "col": 2, "row": 0 }, { "id": "bin", "label": "/dev/null", "kind": "devnull", "col": 2, "row": 2 } ], "edges": [ { "from": "cmd", "to": "scr", "label": "stdout (1)", "kind": "stdout", "stage": 1 }, { "from": "cmd", "to": "bin", "label": "2>/dev/null", "kind": "stderr", "stage": 2 } ] }

Before you run this, decide: the script prints three lines in total. How many will survive, and which ones?

bash report.sh 2>/dev/null

prompt: student@linuxcamp:~/signal$ answer: bash report.sh 2>/dev/null output: report: system nominal report: all checks passed hint: Type bash report.sh, a space, then 2>/dev/null to send channel 2 to the bin: bash report.sh 2>/dev/null

Two lines survived, and they are the two normal ones. The error line is gone. What is left, report: system nominal and report: all checks passed, is the pure stdout (channel 1). You did not change what the program prints; you diverted channel 2 away from the screen. That is the whole trick of redirection: aim a channel somewhere new, by number.

Here is a fact hiding in plain sight. Every output redirect starts with a channel number, even when you do not see one. The plain > operator, which gets its own full lesson next, is really 1>: "take channel 1 and send it somewhere else." The shell just lets you drop the 1.

So >/dev/null is the mirror of what you just ran: it discards the normal output instead of the errors. Before you run this, decide: this time, which single line survives on the screen?

bash report.sh >/dev/null

prompt: student@linuxcamp:~/signal$ answer: bash report.sh >/dev/null output: report: sensor offline hint: A bare > is channel 1. Aim it at the bin: bash report.sh >/dev/null

One line left, and it is the error: report: sensor offline. You discarded channel 1, and the error line never noticed, because it rides channel 2, which you did not touch. Put the two experiments side by side. 2>/dev/null left the two normal lines; >/dev/null left the one error line. Same program, same three lines, and you chose which river reached the screen purely by number.

There is a fancier form you will meet later in this module, in the 2>&1 lesson. It isolates the error channel by re-pointing both channels in one command.

bash report.sh 2>&1 1>/dev/null

On the real machine, that whole run prints exactly one line:

report: sensor offline

You do not need to reproduce this one today, just read it. The shell works through redirections left to right: first 2>&1 points channel 2 at the place channel 1 currently goes, the screen. Then 1>/dev/null re-points channel 1 into the bin. The error keeps the screen; the normal lines vanish. The order of those two pieces matters, which is exactly why it waits for its own lesson.

Stop and look at what you already own. You have seen channel 1 completely alone, and channel 2 completely alone, from the same program, without editing it. The separation was there all along; the numbers let you use it. That is the entire mental model the rest of this module plugs into.

Scaffolding off. From here on, no command is shown.

The script is not special: every Linux command wears the same three channels. Take ls, the directory lister. Ask it for two files at once, one real and one imaginary, with ls fruits.txt nowhere.txt, and it announces the found name on channel 1 while complaining about the missing one on channel 2.

Your goal: run that ls so only the found name prints. Silence the complaint channel the same way you silenced the script's error.

prompt: student@linuxcamp:~/signal$ answer: ls fruits.txt nowhere.txt 2>/dev/null output: fruits.txt hint: Which number is the complaint channel? Aim it at the bin, exactly as you did for report.sh.

fruits.txt, alone. ls announced the found file on channel 1, and its complaint about nowhere.txt went into the bin through channel 2. The channels are not a report.sh gimmick; they are wired into every command on the system. One habit, check the number, works everywhere.

Now flip it, from memory. Same two files, same ls. This time you want the opposite: throw away the found name and let the complaint through, so you can read exactly what ls says when something is missing.

Before you type, decide: which channel number are you discarding, and what does its redirect look like when the number is invisible?

prompt: student@linuxcamp:~/signal$ answer: ls fruits.txt nowhere.txt >/dev/null output: ls: cannot access 'nowhere.txt': No such file or directory hint: The found name rides channel 1, and a bare > means 1. Send it to the bin.

There is the complaint by itself: ls: cannot access 'nowhere.txt': No such file or directory. You discarded channel 1, and channel 2 kept the screen, the same mirror move you ran on report.sh. Read that error once with care: ls names itself, names the file it could not find, and says why. Clean, readable error lines are exactly why errors get their own channel: you can pull them out and study them without the noise.

You earned this card. Three numbers, and the two moves you proved with them:

The one idea under all of it: every command has three numbered channels, 0 in, 1 out, 2 errors. Both output channels default to the screen, so they look mixed, but a number in front of a redirect grabs exactly one of them. Every operator in the rest of this module is just a different way to aim these channels.

When a redirect grabs the wrong stream, look at the number in front of the arrow. No number means 1. Most redirection surprises are just an invisible 1.

You can now read the number on any redirect and say which river it moves. You proved channel 1 and channel 2 are separate, isolated each one from the same program, and then did both again from memory on ls. That is the foundation.

The rest of this module gives every operator its own lesson. > and >> capture output in files, < feeds stdin, 2> files errors away, and /dev/null gets its own deep dive. After that come 2>&1 and &> for merging the channels, then the pipe |, pipe chains, and tee. Each one is a short set of drills on this same signal folder, and each one is just a new way to aim the three numbers you now know.

The module ends with one real Linux machine: the redirection mission lab. There a VM boots just for you with report.sh and the text files in place, and the mission hands you objectives that show no commands. You read the goal, recall the move, and type it. That recall is what makes it stick.

Go on to the next lesson and take the operators one at a time.

Practice File Descriptors: The Three Channels in a real Linux terminal at The Linux Camp. Progress is verified automatically as you type commands on the machine.