In the first lesson I said the shell lets you combine small programs in ways nobody anticipated. This is that lesson.
Everything so far has been individual commands. What makes the command line genuinely powerful — more powerful than any GUI, for a large class of problems — is that its programs are designed to be connected.
Every process starts with three channels already open:
The screen is just the default. Every one of these can be pointed somewhere else, and that's all redirection is.
The split between stdout and stderr is deliberate and useful: it means you can capture a program's results while still seeing its complaints, because they travel on separate channels even though they land in the same place by default.
ls > files.txt # stdout into a file, replacing its contents
ls >> files.txt # stdout appended to the end instead
The difference between > and >> is worth burning in: > truncates the file first. Pointing > at something that already has contents destroys them, instantly, with no warning. It's the second-most common way people lose work at a terminal, after rm.
Errors need their own redirect, because they're on channel 2:
./script.sh 2> errors.txt # errors to a file, output still on screen
./script.sh > out.txt 2>&1 # both into one file
./script.sh &> everything.txt # shorthand for the same thing (bash)
That 2>&1 reads as "send channel 2 to wherever channel 1 is currently going". It looks cryptic and it's worth recognising, because it appears in nearly every cron job and CI script you'll ever read.
And input can come from a file instead of the keyboard:
sort < names.txt
You run echo hello > notes.txt, but notes.txt already contained a week of work. What happened to it?
> sends output to a file. The pipe, |, sends it to another program's input:
ps aux | grep node
ps aux writes a long list to stdout. Instead of your screen, that list becomes grep's stdin. grep reads it, keeps only lines matching "node", and writes those to its stdout — which is your screen.
Neither program knows the other exists. ps doesn't know it's being filtered; grep doesn't know where the text came from. That independence is the whole design, and it's why pipes compose without limit:
cat access.log | grep " 500 " | wc -l
Read left to right: take the log, keep only lines containing a 500 status, count them. Three simple tools answering a question none of them was written for.
This one is a recording — the commands are real, but nothing is running here. Type them into your own shell to follow along.
That second one finds the three IP addresses hitting your server hardest: take each line's first field, sort them, count each unique value, sort those counts numerically in reverse, show the top three. Six tools, one line, no script, no program written.
A small vocabulary covers most real pipelines:
grep pattern — keep matching lines (-v inverts it, -i ignores case, -c counts)wc -l — count linessort — sort lines (-n numerically, -r reversed)uniq -c — collapse adjacent duplicates and count them (needs sorted input)head / tail — first or last N linescut -d, -f2 — pull out a field by delimiterawk '{print $1}' — pull out a column by positionCount how many lines in access.log contain the word "error".
In 1978 Doug McIlroy wrote down the Unix philosophy as: write programs that do one thing well, and write programs to work together on text streams.
The bet was that a small set of sharp tools plus a way to connect them beats a large set of feature-rich ones. Nearly fifty years later that bet keeps paying: the pipeline above analysing a web server log was composed from programs written decades before web servers existed, by people who had no idea what they'd eventually be used for.
That's the real reason to learn this. Not because the commands are hard — they aren't — but because once you think in pipelines, a whole class of problems stops requiring you to write a program at all.
In ps aux | grep node, what is grep reading from?
You now have the model the rest of Linux is built on — a filesystem tree, permissions, processes, and streams you can wire together.
The natural next step is to stop typing these one at a time and start saving them: shell scripts, variables, loops, and conditionals. After that, containers — because a Docker container is, at bottom, a process with its own view of the filesystem, and everything in this course applies directly.
That's what the next courses cover.
