Pipes and redirection
On This Lesson 11 topics
Lesson Overview
So far every command printed its answer on the screen and the answer vanished when you scrolled. This lesson shows two ideas that make the command line far more than a typing replacement for clicking.
Redirection sends a command's output into a file instead of the screen, so you can keep it. Pipes connect two commands, so the output of the first becomes the input of the second, and a big list shrinks to just the lines you care about.
In the picture of the literal assistant: until now you gave one order at a time and listened to the answer. Now you can say "write that answer on a sheet of paper" (redirection), or "give your answer to a second assistant who crosses out everything except the lines with this word" (a pipe).
You need Lessons 2 and 3 (paths, and creating and listing files). Commands appear in Bash and PowerShell. They behave alike in spirit but differ in detail, and the lesson points out where.
Learning Objectives
By the end of this lesson you will be able to:
- Explain what "output" and "input" mean for a command.
- Save a command's output to a file with
>, and add to a file with>>. - Explain the danger of
>replacing a file's old contents. - Connect two commands with
|. - Filter lines with
grep(Bash) orSelect-String(PowerShell). - Build a short pipeline that lists files, sorts them by size, and saves the result.
- Say how Bash pipes and PowerShell pipes differ.
Written Explanation
Output goes somewhere
Every command has a place where its normal output goes. By default, that place is the screen. In Unix terms this is standard output. Errors travel on a separate channel, standard error, which also shows on screen by default. PowerShell has the same idea with named streams: the Success stream (like standard output) and the Error stream.
Because output is a stream and not a fixed picture on a screen, it can be pointed somewhere else.
Redirection with >
The operator > sends output to a file.
$ echo "first line" > notes.txt
$ cat notes.txt
first lineNothing was printed when you ran the first command: the output went into notes.txt. If the file did not exist, it was created. Both shells work this way, and the same command works in PowerShell (Get-Content notes.txt shows the file; so does cat).
The same works for any command:
$ ls > list.txt
PS> Get-ChildItem > list.txtWarning: > replaces the file's old contents, without asking. The Bash manual says the file is truncated to zero size if it already exists, and Microsoft's PowerShell documentation says > overwrites the current contents without warning. A second echo "second" > notes.txt leaves only second in the file.
Appending with >>
>> adds to the end of a file and keeps what was there. If the file does not exist, it is created.
$ echo "first line" > notes.txt
$ echo "second line" >> notes.txt
$ cat notes.txt
first line
second lineRule of thumb: > for "start this file fresh", >> for "keep adding", as in a log.
Redirecting errors
Error messages go to the error channel, so ls missing > out.txt still prints the error on screen and out.txt stays empty. To capture errors, put 2 in front: ls missing 2> errors.txt. This works in Bash and also in PowerShell, where 2 is the number of the Error stream. You will use it rarely at first, but it explains why an error "ignored" your redirection.
Pipes with |
The pipe | connects the output of the command on its left to the input of the command on its right.
$ ls | sortRead it as: "list the folder, then give that list to sort". The pipe does not use a file in between. A pipeline can have many steps: command1 | command2 | command3, processed left to right.
Pipes work in both shells. Microsoft's pipeline documentation defines a pipeline as commands connected by |, each sending its result to the next. The standard Unix idea is the same; I did not re-read the Bash manual's pipeline section in this session.
Filtering: grep and Select-String
The most useful partner for a pipe is a filter that keeps only the lines containing a word.
$ ls | grep log
app.log
log-notes.txtPowerShell's equivalent is Select-String. A small catch: to give it plain lines of text, list names only with -Name:
PS> Get-ChildItem -Name | Select-String loggrep and Select-String also read files directly: grep error app.log and Select-String error app.log search app.log for lines containing error. Select-String prefixes each matching line with the file name and line number when it reads from a file. grep is case-sensitive by default; grep -i ignores case. Select-String is not case-sensitive by default.
Two shells, two kinds of pipe
In Bash, a pipe carries text. Each command in the chain reads lines of text and writes lines of text. In PowerShell, a pipe carries objects: structured things with named properties such as Name and Length. This is why PowerShell can sort files by size without parsing text:
PS> Get-ChildItem | Sort-Object LengthMicrosoft's documentation shows exactly this pattern: get items, filter, sort by a property such as Length. In Bash you use options of the command instead: ls -lS lists a detailed view sorted by size, largest first. To reverse it in PowerShell, add -Descending: Sort-Object Length -Descending.
You do not need to master objects now. The practical rule is: in PowerShell, sort and filter by property name; in Bash, filter text with grep and use the command's own options to order.
Counting with a pipe
Pipes also count. In Bash, wc -l counts lines:
$ ls | wc -l
7In PowerShell, Measure-Object counts the items it receives:
PS> Get-ChildItem -Name | Measure-Objectand its output includes a Count line.
Combining a pipe with redirection
Pipes and > work together. The pipe goes first and redirection last, because redirection captures the final output:
$ ls -lS | grep txt > big-text-files.txt
PS> Get-ChildItem | Sort-Object Length -Descending > sizes.txtA word about file encoding in PowerShell
PowerShell 7 writes redirected output as UTF-8 without a byte-order mark, according to Microsoft's documentation. Older Windows PowerShell may use a different default encoding, so a file written with > might look garbled in another tool. If that happens, Out-File -Encoding utf8 file.txt lets you choose. I did not verify the exact default of every older version.
Examples
Example 1: Save and read (simple)
$ echo "buy milk" > todo.txt
$ echo "call Sam" >> todo.txt
$ cat todo.txt
buy milk
call SamTwo lines were written: > started the file fresh and >> added to it. If todo.txt had already held old notes, the first command would have wiped them.
Example 2: Find a word in a listing (practical)
Suppose a folder holds app.log, server.log, notes.txt, and report.pdf.
$ ls > files.txt
$ grep log files.txt
app.log
server.logPowerShell version:
PS> Get-ChildItem -Name > files.txt
PS> Select-String log files.txtThe second step finds the matches in the file; PowerShell also prints the file name and line number in front of each match. You can skip the file by piping instead: ls | grep log, or Get-ChildItem -Name | Select-String log.
Example 3: A report of the three biggest files (realistic)
You want to know which files are using space.
$ ls -lS | head -4 > biggest.txt
$ cat biggest.txtls -lS lists files with the largest first, including a "total" line at the top. head -4 keeps only the first four lines, which is that total line plus the three biggest files. Then > saves them. PowerShell version:
PS> Get-ChildItem | Sort-Object Length -Descending | Select-Object -First 3 > biggest.txtHere Select-Object -First 3 keeps the first three objects. Because the pipe carries objects, you do not count a "total" line. A caution: this lists subfolders too. A folder has no Length property, so the sort may place folders in an odd position. Try it in a folder of plain files first.
Backend Insight
Backend engineers live in pipes and redirects. A server writes thousands of lines to a log file. You do not read them all: you filter. grep ERROR app.log finds the lines that matter, | wc -l counts them, and > errors.txt saves them for a colleague. A running service often has its output redirected to a log file on purpose, so that it can be read later.
The same two ideas appear later when programs are chained in build and deployment scripts. The core skill is composing small tools: each does one thing, and pipes join them.
Common Mistakes
Mistake 1: Using > when you meant >>
echo "new entry" > log.txt wipes all older entries. When in doubt, use >> or look at the file first.
Mistake 2: Reading and writing the same file in one command
grep error app.log > app.log empties app.log before grep reads it. Send the result to a different file, such as errors.txt.
Mistake 3: Putting the redirect in the wrong place
ls > files.txt | grep log sends the list into the file, and the pipe then receives nothing. Pipes first, redirect last.
Mistake 4: Using Bash tools in PowerShell, or the reverse
grep and wc are not standard in Windows PowerShell. Use Select-String and Measure-Object. In Bash, Select-String does not exist.
Exercises
Try each exercise yourself before opening the solutions.
Level A: Foundation
A1. What is the difference between > and >>?
Show solution A1
A1. > writes output to a file and replaces any existing contents. >> appends to the end and keeps existing contents.
A2. What does the | symbol do?
Show solution A2
A2. It sends the output of the command on its left to the command on its right as input.
A3. Which command keeps only lines containing a word: Bash and PowerShell versions?
Show solution A3
A3. Bash: grep word (for example ls | grep word). PowerShell: Select-String word.
Level B: Application
B1. In a practice folder, create fruit.txt containing three lines (apple, banana, cherry) using echo and redirection. Show your commands.
Show solution B1
B1. echo apple > fruit.txt, then echo banana >> fruit.txt and echo cherry >> fruit.txt. The first command uses > to start the file, the others use >> to add.
B2. Using a pipe, display only the line containing an from fruit.txt (hint: cat or Get-Content first).
Show solution B2
B2. cat fruit.txt | grep an prints banana. PowerShell: Get-Content fruit.txt | Select-String an.
B3. Add a fourth line date to fruit.txt without losing the first three. Which operator did you use?
Show solution B3
B3. echo date >> fruit.txt. The >> operator appends.
Level C: Challenge
C1. Write one pipeline that lists the current folder, keeps only lines containing txt, and saves them to texts.txt.
Show solution C1
C1. Bash: ls | grep txt > texts.txt. PowerShell: Get-ChildItem -Name | Select-String txt > texts.txt.
C2. A teammate wrote grep warning server.log > server.log. Predict what is in server.log afterwards and explain why. Write a correct version.
Show solution C2
C2. The shell opens server.log for writing first, and > truncates it to empty, so grep reads an empty file and finds nothing. Afterwards server.log is empty and the original contents are lost. A correct version: grep warning server.log > warnings.txt.
Mini Challenge
In a practice folder with at least five files of different sizes, use only pipes and redirection to produce a file report.txt that contains:
- Your files listed from largest to smallest.
- Below that, only the lines of that list that contain a word you choose (use
>>for the second part).
Check your work: report.txt has both parts, and you did not overwrite the first part when adding the second.
Summary & Next Lesson
Key takeaways
- Command output goes to the screen by default, and can be redirected.
>writes to a file and replaces its contents.>>appends.|sends one command's output into the next command.grep(Bash) andSelect-String(PowerShell) keep the lines that match.- Pipes carry text in Bash and objects in PowerShell.
- Put the pipe before the redirect, and never read and write the same file in one command.
Next lesson
You can now chain commands and keep their output. The next lesson, "Environment variables in the shell", shows how a shell remembers named values, and how to read and set them for the current session. Module 5 introduced PATH, and now you will set your own.
Self-check
- What does
echo "hi" >> log.txtdo iflog.txthas other lines? - What does
ls | grep txtdo? - Why does
grep x f.txt > f.txtlose data? - Which PowerShell command sorts files by size?
- Which comes last in a pipeline with a redirect: the pipe or the redirect?
Show answers
- It adds
hias a new line at the end and keeps the other lines. - It lists the folder and shows only lines that contain
txt. - The shell empties
f.txtbeforegrepreads it. Get-ChildItem | Sort-Object Length.- The redirect comes last.
References
- Microsoft Learn, about_Redirection. Supports:
>and>>operators, the numbered streams (Success is 1, Error is 2), that>overwrites without warning, that redirection uses UTF8NoBOM in the version documented (PowerShell 7.6), and that>is equivalent to piping toOut-File. - Microsoft Learn, about_Pipelines. Supports: the definition of a pipeline, that PowerShell passes objects one at a time, the
Sort-Object -Property LengthandMeasure-Objectpatterns, and that native command output can be piped into cmdlets such asSelect-String. - man7.org, bash(1) manual page. Supports:
>creates a file or truncates it to zero size, and>>appends and keeps existing content. - Not verified on an official page: the behavior of
grep,wc -l,head,ls -lS,Select-String's case sensitivity and output format, and Bash's|definition (I did not read that section). Older Windows PowerShell's default encoding was not checked either.
Practice
Apply this lesson with the module's lab, exercises and quiz.
Marking a lesson complete is optional and you can undo it any time. Your progress stays in this browser.