LearnLinux FoundationsI/O Redirection

Combining Redirects: Aim Every Stream

> 2> 2>&1 &> | - a hands-on Linux lab on a real virtual machine.

The module's final move: stack the operators you already own. Split a command's results and errors into two files with one line, merge both channels so a pipe can count and search everything, then rearrange the same parts to keep only what matters.

Here is your last work order for this module. Run the status report and file its output: the good news must land in a file called good.txt, the complaint in a file called bad.txt, and the screen must stay completely silent. One command.

Now scan the kit you built across the last eleven lessons. > grabs the results. 2> grabs the errors. /dev/null shreds a stream. The pipe hands output to another command. Every one of them re-aims exactly ONE channel. So which single operator files two channels into two different files? None. Alone, none of them can do it.

That is the final secret of the module, and it is a good one: the operators were never meant to work alone. This lesson is where they combine.

The black boxes below are a practice terminal: a safe sandbox that only checks the one command each step asks for, so you cannot break anything. You are still in the signal folder from the rest of the module, with report.sh, the script that always prints two normal lines on stdout and one complaint on stderr. On the real machine at the end of the module, the mission builds this same folder and you run these moves for real.

This lesson adds exactly one new rule, and everything else follows from it: when the shell reads your command line, it applies every redirection it finds. Redirections are not either-or. One line can carry two of them, or three, plus a pipe, and each one quietly re-aims its own channel before the command even starts.

A quick roll-call of what you already own. Each of these had its own lesson, so this is a reminder, not a lesson.

Two combinations matter more than all the rest, and they are the two halves of this lesson: the split, each channel to its own file, and the merge, both channels down one pipe.

The stacking is original equipment, not a later trick. The numbered forms like 2> arrived with the Bourne shell in Unix Version 7 in 1979, and its manual showed them combined on one line from the start. You are about to type a shape that has worked, unchanged, for nearly fifty years.

Back to the work order. You know > good.txt claims channel 1. You know 2> bad.txt claims channel 2. The move is to put both on the same line, side by side. Each redirect grabs its own channel, and neither interferes with the other:

{ "caption": "bash report.sh > good.txt 2> bad.txt: two redirects ride one line, each grabs its own channel, and nothing is left for the screen.", "nodes": [ { "id": "cmd", "label": "report.sh", "kind": "command", "col": 0, "row": 1 }, { "id": "good", "label": "good.txt", "kind": "file", "col": 4, "row": 0 }, { "id": "bad", "label": "bad.txt", "kind": "file", "col": 4, "row": 2 } ], "edges": [ { "from": "cmd", "to": "good", "label": "> stdout (1)", "kind": "stdout", "stage": 1 }, { "from": "cmd", "to": "bad", "label": "2> stderr (2)", "kind": "stderr", "stage": 2 } ] }

bash report.sh > good.txt 2> bad.txt

Before you run it, commit to a prediction. The report always produces three lines. How many will reach the screen this time: three, two, one, or none?

prompt: student@linuxcamp:~/signal$ answer: bash report.sh > good.txt 2> bad.txt output: hint: One redirect per channel, side by side: bash report.sh > good.txt 2> bad.txt

None. The screen shows nothing at all, and that silence is the success signature: channel 1 was aimed at good.txt and channel 2 at bad.txt, so no line had any way to reach you. But silence is a claim, not evidence. If the split really worked, two lines of good news and one complaint are sitting in two separate files right now, and your next job is to catch them there.

Read both files back with cat. Before each one, commit to what you expect: good.txt should hold exactly the two stdout lines, bad.txt exactly the one stderr line, with no mixing in either direction.

prompt: student@linuxcamp:~/signal$ answer: cat good.txt output: report: system nominal report: all checks passed hint: Print the stdout file: cat good.txt

prompt: student@linuxcamp:~/signal$ answer: cat bad.txt output: report: sensor offline hint: Print the stderr file: cat bad.txt

A perfect split. Two normal lines in good.txt, one complaint in bad.txt, zero leakage between them. Stop and look at what you just did: with one line, you separated a command's results from its errors at the moment they were born. This is the everyday shape of real operations work. Long jobs everywhere run tonight as job > results.log 2> errors.log, so that tomorrow someone can read the wins and the failures each on their own. The work order from the cold open is complete.

The second combination solves the pipe's blind spot. The pipe lesson taught you that | carries channel 1 and nothing else. Pipe the report into the line counter wc -l and the counter never sees the complaint; it slips past the pipe and splashes onto your screen instead. The fix comes from the 2>&1 lesson: fold channel 2 into channel 1 first, THEN pipe. Merged before the pipe, everything rides together:

{ "caption": "bash report.sh 2>&1 | wc -l: 2>&1 points channel 2 at the same place as channel 1, so the pipe carries all three lines into wc -l.", "nodes": [ { "id": "cmd", "label": "report.sh", "kind": "command", "col": 0, "row": 1 }, { "id": "out", "label": "stdout (1)", "kind": "stdout", "col": 2, "row": 0 }, { "id": "err", "label": "stderr (2)", "kind": "stderr", "col": 2, "row": 2 }, { "id": "wc", "label": "wc -l", "kind": "command", "col": 4, "row": 1 } ], "edges": [ { "from": "cmd", "to": "out", "label": "results", "kind": "stdout", "stage": 1 }, { "from": "cmd", "to": "err", "label": "complaint", "kind": "stderr", "stage": 1 }, { "from": "out", "to": "wc", "label": "|", "kind": "stdout", "stage": 2 }, { "from": "err", "to": "wc", "label": "2>&1 |", "kind": "stderr", "stage": 2 } ] }

bash report.sh 2>&1 | wc -l

Before you run it, commit to a number. The report emits two lines on channel 1 and one on channel 2. With the merge in place, will wc -l print 2 or 3?

prompt: student@linuxcamp:~/signal$ answer: bash report.sh 2>&1 | wc -l output: 3 hint: Fold channel 2 into channel 1 before the pipe: bash report.sh 2>&1 | wc -l

3, not 2. That one-line difference is the whole point. Without 2>&1 the pipe would carry only the two stdout lines, wc -l would answer 2, and the complaint would splash onto your screen, uncounted. By merging first, the error line rode the pipe with the results and got counted with them. This is the reflex to build: whenever a command after a pipe needs to see errors too, 2>&1 goes on before the |.

You now hold both combining moves, and every remaining shape is just the same parts rearranged. So, no more walkthroughs. Three work orders remain, no commands shown. Everything you need is already in your hands.

First order: somewhere in the report's three lines is the one that mentions offline. Print that line and nothing else. Use grep, the line filter you met in the 2>&1 lesson (its full lesson comes in the text-processing module): grep offline keeps only the lines containing the word offline. One catch, and it decides everything: the line you are hunting lives on the error channel, and you know what a pipe carries on its own.

prompt: student@linuxcamp:~/signal$ answer: bash report.sh 2>&1 | grep offline output: report: sensor offline hint: The pipe alone carries channel 1. Fold channel 2 in first, then filter for the word offline.

Found it. The complaint could only surface because you merged before the pipe: the filter finally had both channels flowing through it. Skip the merge and this command prints nothing at all, while the complaint splashes onto the screen unfiltered. You just built, from memory, the exact shape engineers type when they search a noisy command for its errors.

Second order: a long job runs overnight with nobody watching. Capture every line the report produces, results and complaints together, into one file named all.txt. You own a shorthand built for exactly this job; it is the shortest thing in your kit. Then produce the evidence: print the archive and confirm all three lines are inside.

prompt: student@linuxcamp:~/signal$ answer: bash report.sh &> all.txt ||| bash report.sh > all.txt 2>&1 output: hint: The bash shorthand that aims both channels at one file, then the filename.

prompt: student@linuxcamp:~/signal$ answer: cat all.txt output: report: system nominal report: all checks passed report: sensor offline hint: The file-printing command you have used all module, pointed at the archive.

All three lines, in a stable order. The two stdout lines come first, then the complaint, because a file records the merged stream in the order it was written. &> all.txt is the shorthand; > all.txt 2>&1 lands the same result the long way, and either earns the checkmark. Screen kept clean, nothing lost. That is the overnight-job shape.

Last order, and the strictest. Tonight nobody cares about good news: the report's normal output must vanish forever, unread. The complaint must be saved in a file named onlyerr.txt. And the screen must show nothing at all. Every piece of this came from a different lesson; the arrangement is yours to build. Then prove it worked: print the file and confirm it holds exactly one line.

prompt: student@linuxcamp:~/signal$ answer: bash report.sh >/dev/null 2> onlyerr.txt ||| bash report.sh 2> onlyerr.txt >/dev/null output: hint: Aim channel 1 at the shredder and channel 2 at the file, side by side on one line.

prompt: student@linuxcamp:~/signal$ answer: cat onlyerr.txt output: report: sensor offline hint: Print onlyerr.txt and count what you see.

One line, exactly as ordered: the good news went into the void, the complaint into the file, and the screen stayed dark. Look at the shape you built. It is the split from the start of this lesson with one destination swapped for /dev/null. Same parts, new arrangement, different job. That is the skill this module was building the whole time: you do not memorize ten incantations, you aim two channels at the destinations you choose.

You earned this cheat sheet. Every row is a combination you built yourself in the signal folder, from moves the earlier lessons handed you one at a time.

The one idea underneath all of it: a command has three channels, input (0), output (1), and error (2). Every operator you own re-aims exactly one of them, and the shell applies every redirection on the line. Once you see the channels, a monster like job >/dev/null 2>&1 stops being symbols to memorize and becomes a sentence you can read: output to the void, errors right behind it.

When a command's output vanishes and you did not expect it to, check the line for a stray > or 2>: you may have aimed a stream at a file. And when a pipe seems to lose the errors, remember that | carries only channel 1; put 2>&1 before the pipe to bring them along.

This was the last lesson of the I/O Redirection module, and it ends the way the module began: three channels, and you deciding where each one goes. You split a command's two output channels into two files with one line. You merged them so a counter and a filter could see everything. Then you rearranged the same parts to keep only what mattered, and you filled all three challenge orders from memory, with no commands shown.

One thing remains: the redirection mission lab, on a real Linux machine. A VM boots just for you with this same signal folder and report.sh in place, and the mission hands you objectives drawn from the whole module, single moves and combinations alike. No commands are shown there either. You read the objective, recall the move, and type it. That recall is what makes it stick.

Go aim the streams for real.

Practice Combining Redirects: Aim Every Stream in a real Linux terminal at The Linux Camp. Progress is verified automatically as you type commands on the machine.