LearnLinux FoundationsI/O Redirection

Split the Stream with tee

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

End the see-it-or-save-it trade. Watch a line land on screen and in a file at once with tee, snapshot a pipeline mid-flight without disturbing its result, and grow a log with tee -a, all in the practice terminal.

All module long you have been living with a quiet trade. Remember from the > lesson: point a command's output at a file and the words are saved, but your screen goes dark while it happens. See the output or keep it. Never both.

Feel the trade one more time, on purpose. You are in the signal folder from the earlier lessons, with fruits.txt and its neighbors around you. Before you run this, decide: when the command finishes, will the word hi be on your screen, in the file, or in both places?

echo hi > out.txt

prompt: student@linuxcamp:~/signal$ answer: echo hi > out.txt output: hint: The overwrite arrow from the > lesson: echo hi > out.txt

In the file, only. The screen shows nothing, because > re-points the whole output stream into out.txt and leaves nothing behind for your eyes. To know what landed you would need a second command, cat out.txt, after the fact. For a one-word echo, that is a shrug. For a long-running job, it means minutes of staring at a blank screen, hoping the file is filling. This lesson ends the trade: one command that saves the stream AND lets you watch it flow by.

The black boxes here 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.

The fix is a command called tee. It sits in the middle of a stream and does exactly two things: it writes a copy of everything flowing through it into a file, and it passes the same lines onward, untouched. It never edits, never filters, never holds anything back. Save and see, at the same time.

You connect it with the pipe from the | lesson: the | operator feeds the output of the command on its left into the command on its right. So echo saved | tee tee1.txt sends the word saved into tee, and tee both writes it into tee1.txt and prints it on.

The name is plumbing. A T fitting is the pipe joint shaped like the letter T that splits one flow into two directions. The tee command has been doing the same job for text since the early 1970s, when Unix pipelines were brand new.

Before you run it, commit to a prediction: with tee in the picture instead of >, will the screen stay blank the way it did in the cold open?

echo saved | tee tee1.txt

prompt: student@linuxcamp:~/signal$ answer: echo saved | tee tee1.txt output: saved hint: Pipe echo into tee, then name the file: echo saved | tee tee1.txt

There it is: saved, printed to your screen, which is exactly what > refused to do. That is half of tee's promise kept. The other half is the claim that the same word is ALSO sitting inside tee1.txt right now. Do not take a command's word for it. Check the file.

Read the file back with cat, the file-printing command you have used since the reading lessons. Decide first what you expect to find: an empty file, or the word you just watched go by?

cat tee1.txt

prompt: student@linuxcamp:~/signal$ answer: cat tee1.txt output: saved hint: cat, a space, then the file tee just wrote: cat tee1.txt

One word, two destinations. The screen got it live, and the file got a permanent copy, from a single command. That is the whole contract of tee. One habit to note now: run plain tee tee1.txt again and the file starts over, wiped and rewritten, exactly like the single > arrow. Hold that thought; the append switch is coming. First, the form that makes tee famous.

At the end of a pipe, tee is convenient. In the MIDDLE of a pipe, it is the tool engineers reach for. Because tee passes its input through untouched, you can splice it between two stages of a pipeline and grab a snapshot of the data mid-flight, without disturbing the final result.

Here is the shape of the move you are about to run. Watch the stream split at the T:

{ "caption": "cat fruits.txt | tee copy.txt | wc -l: one stream flows in, two identical streams flow out, one into copy.txt, one onward to wc -l.", "nodes": [ { "id": "cat", "label": "cat fruits.txt", "kind": "command", "col": 0, "row": 1 }, { "id": "tee", "label": "tee copy.txt", "kind": "command", "col": 2, "row": 1 }, { "id": "wc", "label": "wc -l", "kind": "command", "col": 4, "row": 0 }, { "id": "copy", "label": "copy.txt", "kind": "file", "col": 4, "row": 2 } ], "edges": [ { "from": "cat", "to": "tee", "label": "| stdout (1)", "kind": "stdout", "stage": 1 }, { "from": "tee", "to": "wc", "label": "| same lines", "kind": "stdout", "stage": 2 }, { "from": "tee", "to": "copy", "label": "a copy", "kind": "stdout", "stage": 2 } ] }

Remember from the | lesson: cat fruits.txt | wc -l printed 5, the line count of the fruit list. Now decide two things before you run the tapped version: what number will print this time, and what will exist on disk afterwards that did not exist before?

cat fruits.txt | tee copy.txt | wc -l

prompt: student@linuxcamp:~/signal$ answer: cat fruits.txt | tee copy.txt | wc -l output: 5 hint: Splice tee copy.txt between the two stages: cat fruits.txt | tee copy.txt | wc -l

Still 5. Put this run next to the pipe lesson's run and the screens are identical, as if tee were not there at all. That sameness is the point: tee did not add, drop, or change a single line on its way to wc -l. The difference is not on the screen. It is on disk, where a file named copy.txt now exists holding everything that flowed past the T. Go look.

Before you open copy.txt, commit: how many lines should be inside it? The stream that passed through tee was the whole fruit list, so the snapshot should be the whole fruit list.

cat copy.txt

prompt: student@linuxcamp:~/signal$ answer: cat copy.txt output: apple banana cherry date elderberry hint: Read the file tee wrote mid-pipe: cat copy.txt

All five fruits, the exact data that crossed that point in the pipeline. This is the workhorse form of tee, and it is how engineers debug long pipelines in real work. When a five-stage chain produces a weird answer, they splice tee between stages to capture what the data looked like at each hop, without changing the final result. You now hold both forms of the command: tee at the end of a pipe to watch and save, tee in the middle to snapshot and pass on.

One flag completes the kit. Plain tee file starts its file over on every run, wiping what was there, the way > does. When you want the file to GROW instead, add -a, short for append: tee -a file adds the new lines under the old ones and erases nothing.

It is the same choice you already know from the redirect lessons, carried into the world of pipes: plain tee is the single arrow >, and tee -a is the double arrow >>. Overwrite for a fresh snapshot, append for a running log.

No more walkthroughs. Right now tee1.txt holds the one line saved from earlier. The rest of the lesson is yours: three jobs, no commands shown.

A teammate wants to know how many lines fruits.txt has, and wants a copy of the list saved into copy.txt at the same time. One command, one pass through the data.

prompt: student@linuxcamp:~/signal$ answer: cat fruits.txt | tee copy.txt | wc -l output: 5 hint: Print the file into a pipe, splice the copier in the middle, count at the end.

Count delivered, copy saved, data read once. That mid-pipe splice from memory is the move that makes teammates ask how you did two things with one command.

tee1.txt is now a log, and logs must never forget. Add the word more underneath the saved line already in the file, without erasing anything, and watch the word on screen as it lands.

prompt: student@linuxcamp:~/signal$ answer: echo more | tee -a tee1.txt output: more hint: Pipe echo into tee, and put the append switch between tee and the filename.

The word more crossed your screen on its way into the file, and the append switch should have kept the old line alive underneath it. Should have. A claim like that deserves evidence, and producing it is your last job.

Prove the append preserved history. Print the log file and confirm both entries survived: the old line on top, the new line underneath.

prompt: student@linuxcamp:~/signal$ answer: cat tee1.txt output: saved more hint: The reading command you have used all module, pointed at the log file.

saved, then more. The -a flag added without erasing, and you caught the whole history in one read. Three jobs, zero prompting. You split streams from memory now, which is the moment tee stops being trivia and becomes a tool.

You earned this cheat sheet. Every row is something you just ran in the signal folder:

The one idea under all of it: tee is a T fitting for text. One stream flows in, two identical streams flow out, one into a file, one onward to the screen or the next command. It never filters and never edits. When you want to keep your eyes on live output without giving up the saved copy, or capture the middle of a pipeline without breaking it, tee is the move.

Plain tee overwrites its file on every run, like >. Reach for tee -a whenever yesterday's lines still matter, the same instinct that sends you to >> instead of >.

You can now split a stream three ways from memory. command | tee file watches and saves at once, tee spliced mid-pipe snapshots data without disturbing the result, and tee -a grows a log instead of wiping it. The trade from the cold open is dead: you never again have to choose between seeing output and keeping it.

The I/O Redirection module ends with one real Linux machine: the redirection mission lab. There a VM boots just for you with this same signal folder and its files in place, and the mission hands you objectives with no commands shown. You read the objective, recall the move, and type it. That recall is what makes it stick.

Finish the other I/O Redirection lessons, then go split some streams for real.

Practice Split the Stream with tee in a real Linux terminal at The Linux Camp. Progress is verified automatically as you type commands on the machine.