Learn › Linux Foundations › I/O Redirection
| - a hands-on Linux lab on a real virtual machine.
Meet the pipe: the operator wires one command's stdout into the next command's stdin, no file in between. Count lines, trim a stream, and transform text mid-flow, then run all three pipes again from memory in the practice terminal.
Earlier in this module you counted the lines of fruits.txt by feeding the file into a command: wc -l < fruits.txt. The < operator worked because the text you wanted was already sitting in a file.
But most of the time the text you want to work on is not in a file. It is the output of another command. You could catch that output in a temporary file with >, then feed the file into the next command with <, then delete the file. Three steps and a leftover file, every single time.
Linux has a better answer. One character joins any two commands so the output of the first flows straight into the second, with no file in between. That character is |, the pipe, and it is the operator this whole module has been building toward.
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. You are working inside the signal folder in your home directory, the small fixture folder this module uses. On the real machine at the end of the module, the capstone mission builds this same signal folder and you run these moves for real.
A pipe, written |, connects two commands. It takes the stdout of the command on its left and feeds it into the stdin of the command on its right.
Quick reminder from the start of this module: every command has numbered channels. Channel 0 is stdin, where a command reads input from. Channel 1 is stdout, where it writes normal output. The pipe simply wires channel 1 of the left command to channel 0 of the right one. No file is created, nothing is saved. The data flows through and is gone.
Doug McIlroy at Bell Labs spent years arguing that programs should snap together like segments of garden hose. In 1973 Ken Thompson finally added the pipe to Unix in a single night, and the | notation has not changed since.
Here is the shape: a command, then |, then another command. cat fruits.txt prints the file to its stdout; the | carries that stdout into wc -l as its input; wc -l counts the lines and prints one number. Watch the data flow left to right through each stage:
{ "caption": "cat fruits.txt | wc -l: the pipe carries stdout straight into the next command's stdin, no file in between.", "nodes": [ { "id": "cat", "label": "cat fruits.txt", "kind": "command", "col": 0, "row": 0 }, { "id": "wc", "label": "wc -l", "kind": "command", "col": 2, "row": 0 }, { "id": "screen", "label": "screen", "kind": "screen", "col": 4, "row": 0 } ], "edges": [ { "from": "cat", "to": "wc", "label": "| stdout (1)", "kind": "stdout", "stage": 1 }, { "from": "wc", "to": "screen", "label": "5", "kind": "stdout", "stage": 2 } ] }
Time to run it. Count the lines of fruits.txt by piping cat into wc -l:
cat fruits.txt | wc -l
Before you press Enter, commit to a prediction. In the < lesson you saw two forms: wc -l fruits.txt printed 5 fruits.txt, and wc -l < fruits.txt printed a bare 5. Which one will the pipe behave like: filename shown, or not?
prompt: student@linuxcamp:~/signal$ answer: cat fruits.txt | wc -l output: 5 hint: Put a pipe between the two commands: cat fruits.txt | wc -l
A bare 5, no filename. That is the tell. wc read from its stdin, exactly as it did with <, so it never learned a filename. The pipe is the same idea as <, feeding stdin, but the feed comes from a command instead of a file. cat printed five lines, the pipe carried them across, and wc -l counted what arrived. No temporary file, nothing to clean up.
A pipe fits between any two commands where the left one produces text and the right one consumes it. Swap the counting stage for a trimming one. head -3 keeps the first three lines of whatever it receives and drops the rest.
cat fruits.txt | head -3
Before you run it, decide exactly which three lines will appear. The file holds apple, banana, cherry, date, and elderberry, in that order.
prompt: student@linuxcamp:~/signal$ answer: cat fruits.txt | head -3 output: apple banana cherry hint: Pipe cat into head -3 to keep the first three lines: cat fruits.txt | head -3
The first three names, and nothing after cherry. Same left side as before, cat fruits.txt, but a different right side gave you a completely different tool. That is the pattern to notice: you did not learn a new command for "show the top of a file's output." You composed two commands you already knew, and the pipe did the wiring.
Nothing in a pipe needs to touch a file. Any command that writes to stdout can feed any command that reads stdin. Here echo hello produces the word hello, and tr a-z A-Z translates every lowercase letter in its input to uppercase.
echo hello | tr a-z A-Z
Before you run it, commit: what exact text will land on the screen?
prompt: student@linuxcamp:~/signal$ answer: echo hello | tr a-z A-Z output: HELLO hint: Pipe echo into tr with the two ranges: echo hello | tr a-z A-Z
HELLO. The word never existed in any file. It was born on echo's stdout, traveled through the pipe, and was rewritten by tr on the way to your screen. This is the milestone of the lesson: you are no longer running commands one at a time. You are building a bigger tool out of small ones, which is the design idea Unix was built on.
One boundary to lock in before the challenges. A pipe carries only stdout, channel 1. Error messages travel on stderr, channel 2, and sail right past the pipe to your screen. Remember the 2>&1 lesson: merging the channels first is how you send errors through a pipe too. That move gets its full workout in the capstone.
Scaffolding off. No command is shown from here on.
Produce the number of lines in fruits.txt, just the number with no filename after it, using a pipe. Two commands, one | between them.
prompt: student@linuxcamp:~/signal$ answer: cat fruits.txt | wc -l output: 5 hint: The command that prints a file, the pipe, then the command that counts lines.
5, bare, exactly as a pipe delivers it. cat fed the file into the pipe, wc -l counted what came through, and no filename appeared because wc read from stdin. That is your first pipe from memory.
Still no command shown. Show only the first three fruit names from fruits.txt, using a pipe to trim the stream.
prompt: student@linuxcamp:~/signal$ answer: cat fruits.txt | head -3 output: apple banana cherry hint: Print the file into the pipe, then keep the first three lines on the other side.
Three names, list trimmed. Same wiring as the count, different right-hand tool. You are choosing the second stage to match the job now, which is the whole skill.
One more, from memory. Make the terminal print HELLO in capitals. Start from echo hello and let a pipe do the shouting. Remember the translate command takes two letter ranges: the one to replace and the one to replace it with.
prompt: student@linuxcamp:~/signal$ answer: echo hello | tr a-z A-Z output: HELLO hint: echo the word, pipe it, then translate the lowercase range to the uppercase range.
HELLO, built entirely in the stream. Three challenges, three pipes, zero files. The reflex you just built, output of one command becomes input of the next, is the one you will use more than any other single idea in this module.
You earned this cheat sheet. Every row is a pipe you just ran in the signal folder.
The one idea under all of it: | wires the stdout (channel 1) of the left command into the stdin (channel 0) of the right command. No file is created and nothing is saved. Any command that produces text can feed any command that consumes it.
A pipe carries only stdout. If a command's error message still lands on your screen after the pipe, that is stderr doing what it always does, going straight to the terminal. Merge with 2>&1 before the | when you want errors to ride along, exactly as the 2>&1 lesson showed.
You just ran the pipe through its full single-stage range: feeding a counter, trimming a stream, and transforming text that never touched a file, then all three again from memory.
Two lessons in this module pick up directly from here. Pipe chains strings three or more commands together, each stage handing its output to the next. tee saves a copy of the stream to a file without stopping the flow.
The io-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 the mission hands you objectives that use exactly what you just ran, with no commands shown. You read the objective, recall the operator, and type it. That recall is what makes it stick.
Finish the module's lessons, then go wire the streams for real.
Practice The Pipe: Feed One Command into Another in a real Linux terminal at The Linux Camp. Progress is verified automatically as you type commands on the machine.