4/10 Terminal Mastery
Pipes, Redirection and Command Chaining: Mastering Terminal Workflows
So far in this series, we've learned how to navigate the file system, create and manage files, and search through their contents.
But there's a skill that takes terminal usage to another level:
Combining commands to build powerful workflows.
Instead of running one command, reading its output, and manually passing the result to another command, we can connect commands together, redirect their output, and automate sequences of operations.
In this article, we'll explore three essential concepts:
- Pipes (
|) - Input and output redirection (
<,>,>>) - Command chaining (
&&,||,;)
We'll cover Windows PowerShell, Windows CMD, macOS, and Linux.
Let's get started.
1. Understanding Standard Input and Output
Before learning pipes and redirection, we need to understand how command-line programs communicate.
Most command-line applications use three standard streams:
| Stream | Number | Purpose |
|---|---|---|
| Standard Input | 0 | Receives input |
| Standard Output | 1 | Displays normal output |
| Standard Error | 2 | Displays error messages |
Think of them as three separate communication channels.
For example, when you run:
ls The command sends its normal output to Standard Output.
But if you run a command that fails, its error message usually goes to Standard Error.
This distinction becomes extremely useful when you want to save successful results without saving error messages, or the other way around.
2. What Is a Pipe?
A pipe connects the output of one command to the input of another.
The symbol is:
| Imagine you have a list of files and want to search that list for a specific filename.
On Linux and macOS:
ls -la | grep "report" Here's what happens:
-
ls -lalists directory contents. - The pipe (
|) takes its output. -
grep "report"searches that output. - Only matching lines are displayed.
Instead of manually copying the output of one command into another, the shell connects them automatically.
Pipes in Windows PowerShell
PowerShell also supports pipes:
Get-ChildItem | Where-Object { $_.Name -like "*report*" } This example:
-
Get-ChildItemretrieves directory items. -
Where-Objectfilters them. -
$_represents the current object being processed. -
.Nameaccesses the item's name.
Here's an important difference:
Bash and Zsh generally pass streams of text or bytes through pipes. PowerShell pipelines pass .NET objects between PowerShell commands.
That means PowerShell can filter and manipulate properties directly without first parsing text.
Pipes in Windows CMD
CMD also supports pipes:
dir | findstr "report" This works similarly to the Linux example:
-
dirlists directory contents. -
findstrsearches the output. - Matching lines are displayed.
Connecting Multiple Commands
You can connect more than two commands.
Linux and macOS:
ls -la | grep "\.log" | sort PowerShell:
Get-ChildItem | Where-Object Extension -eq ".log" | Sort-Object Name CMD:
dir /b | findstr "\.log" | sort This is where the terminal becomes powerful: each command performs one small task, and together they solve a larger problem.
3. Redirecting Output to a File
By default, commands usually display their output in the terminal.
But what if you want to save that output into a file?
That's where redirection comes in.
The > Operator: Overwrite a File
Linux and macOS:
ls -la > files.txt PowerShell:
Get-ChildItem > files.txt CMD:
dir > files.txt All three examples redirect normal output into files.txt.
Important: If the file already exists, > replaces its previous contents.
Be careful when using it with important files.
The >> Operator: Append to a File
What if you want to add new output without deleting the existing contents?
Use >>.
Linux and macOS:
echo "New log entry" >> app.log PowerShell:
"New log entry" >> app.log CMD:
echo New log entry >> app.log This adds the new content to the end of the file.
A useful way to remember the difference:
-
>replaces existing content. -
>>appends new content.
4. Redirecting Error Messages
Remember the three standard streams?
We can redirect them separately.
The number 2 represents Standard Error.
For example, in Bash and Zsh:
ls /this-folder-does-not-exist 2> errors.txt PowerShell:
Get-ChildItem C:\ThisFolderDoesNotExist 2> errors.txt CMD:
dir C:\ThisFolderDoesNotExist 2> errors.txt Instead of displaying the error in the terminal, the error output is redirected to errors.txt.
Redirecting Both Normal Output and Errors
Sometimes you want to capture everything.
In Bash and Zsh:
some-command > output.txt 2>&1 This means:
- Redirect Standard Output to
output.txt. - Redirect Standard Error to wherever Standard Output is currently going.
The order matters.
In PowerShell:
some-command *> output.txt The *> operator redirects all PowerShell streams to the specified destination.
In Windows CMD:
some-command > output.txt 2>&1 The CMD syntax is the same as Bash for this operation.
A useful detail: In PowerShell, 2>&1 merges the error stream into the success output stream. It doesn't mean exactly the same thing as redirecting every PowerShell stream with *>.
5. Redirecting Input from a File
We've seen how to redirect command output into a file.
But can we do the opposite?
Can a command receive its input from a file?
In Bash and Zsh:
sort < names.txt The shell sends the contents of names.txt to the command's standard input.
In CMD:
sort < names.txt The same syntax works.
What About PowerShell?
PowerShell doesn't use < as a general native stdin redirection operator in the same way.
Instead, you can use the pipeline:
Get-Content names.txt | Sort-Object Or, for a native executable that reads standard input:
Get-Content names.txt | some-native-command This distinction matters when working across different shells.
6. The tee Command: Display and Save Output
Sometimes you want to see command output in the terminal and save it to a file at the same time.
On Linux and macOS, use tee:
ls -la | tee files.txt The output is displayed in the terminal and written to files.txt.
To append instead of replacing the file:
ls -la | tee -a files.txt The PowerShell Equivalent
PowerShell provides Tee-Object:
Get-ChildItem | Tee-Object -FilePath files.txt To append:
Get-ChildItem | Tee-Object -FilePath files.txt -Append What About CMD?
CMD doesn't include a built-in equivalent of tee.
You can use PowerShell for this task or install an external utility that provides similar functionality.
7. Command Chaining: Running Commands Together
So far, we've connected commands using pipes.
But sometimes we don't want to pass output between commands.
We simply want to execute multiple commands in sequence.
That's called command chaining.
There are three important operators:
| Operator | Meaning |
|---|---|
; | Run commands sequentially |
&& | Run the next command only if the previous command succeeds |
| ` |
Let's understand each one.
The {% raw %}; Operator
In Bash, Zsh, and PowerShell 7+:
mkdir project; cd project; touch README.md The commands execute sequentially.
The next command runs regardless of whether the previous command succeeded.
In CMD, use & instead:
mkdir project & cd project & echo Hello > README.md Important: ; is not the general-purpose command separator in Windows CMD.
The && Operator
This operator runs the next command only if the previous command succeeds.
Linux and macOS:
mkdir project && cd project && touch README.md PowerShell 7+:
New-Item -ItemType Directory project && Set-Location project CMD:
mkdir project && cd project && echo Hello > README.md If the directory creation fails, the next command won't execute.
This is useful when later commands depend on earlier ones.
The || Operator
This operator runs the next command only if the previous command fails.
Bash and Zsh:
cd missing-folder || echo "Directory not found" PowerShell 7+:
Set-Location missing-folder || Write-Output "Directory not found" CMD:
cd missing-folder || echo Directory not found This is especially useful for simple fallback behavior and error messages.
A Note About Older PowerShell Versions
Native && and || support was introduced in PowerShell 7.
If you're using Windows PowerShell 5.1, these operators aren't available as command-chain operators.
You can use conditional logic instead:
New-Item -ItemType Directory project if ($?) { Set-Location project } For native executables, $LASTEXITCODE is often the better choice when you need to check the program's exit code.
8. Understanding Exit Codes
How does a shell know whether a command succeeded?
Most command-line programs return an exit code when they finish.
Conventionally:
-
0means success. - A non-zero value means an error or another unsuccessful result.
Bash and Zsh
Check the previous command's exit code:
echo $? PowerShell
PowerShell has two useful variables:
$? This indicates whether the previous command succeeded from PowerShell's perspective.
For native executables, use:
$LASTEXITCODE It contains the exit code returned by the most recently executed native program.
Windows CMD
Use:
echo %ERRORLEVEL% These values are important when building scripts and automated workflows.
9. Real-World Examples
Let's combine everything we've learned.
Example 1: Save a Directory Listing
Linux and macOS:
ls -la > directory-report.txt PowerShell:
Get-ChildItem | Out-File directory-report.txt CMD:
dir > directory-report.txt Example 2: Search for Errors in a Log File
Linux and macOS:
grep -i "error" app.log > errors.txt PowerShell:
Select-String -Path app.log -Pattern "error" | Out-File errors.txt CMD:
findstr /i "error" app.log > errors.txt Example 3: Create a Project Structure
Bash and Zsh:
mkdir my-project && cd my-project && mkdir src docs tests PowerShell 7+:
New-Item -ItemType Directory my-project && Set-Location my-project && New-Item -ItemType Directory src, docs, tests CMD:
mkdir my-project && cd my-project && mkdir src docs tests Example 4: Keep a Record of Command Output
Linux and macOS:
some-command 2>&1 | tee command.log PowerShell:
some-command *>&1 | Tee-Object -FilePath command.log These examples are useful when troubleshooting applications or keeping records of command execution.
10. Common Mistakes to Avoid
1. Confusing > with >>
Using > accidentally can erase existing file contents.
2. Assuming every shell behaves the same way
PowerShell, CMD, Bash, and Zsh have different features and syntax.
3. Forgetting the order of redirection
In Bash and CMD:
command > output.txt 2>&1 is not equivalent to:
command 2>&1 > output.txt Redirections are processed from left to right.
4. Using && in Windows PowerShell 5.1
Remember that native && and || support requires PowerShell 7 or later.
5. Assuming a pipe always passes plain text
PowerShell pipelines can pass structured objects, while traditional Unix pipelines generally pass byte streams.
11. Practice Exercises
Try these exercises in your preferred terminal.
Exercise 1: List the files in your current directory and save the result to files.txt.
Exercise 2: Append a new line to that file without deleting its existing content.
Exercise 3: Search a log file for the word error and save matching results into another file.
Exercise 4: Create a directory named terminal-practice, enter it, and create three subdirectories: src, docs, and tests.
Exercise 5: Run a command that intentionally fails and redirect its error output into a file.
Exercise 6: Use a pipe to filter a directory listing by filename.
Try implementing each exercise in Bash or Zsh, PowerShell, and CMD. Pay attention to the differences.
Quick Reference
| Task | Bash / Zsh | PowerShell | CMD |
|---|---|---|---|
| Pipe | `\ | ` | `\ |
| Overwrite output | {% raw %}> | > | > |
| Append output | >> | >> | >> |
| Redirect errors | 2> | 2> | 2> |
| Run sequentially | ; | ; (7+ also) | & |
| Run on success | && | && (7+) | && |
| Run on failure | ` | ` | |
| Display and save | tee | Tee-Object | External utility / PowerShell |
What's Next?
You've now learned how to combine commands, redirect their output, handle errors, and build more efficient terminal workflows.
In the next article, we'll explore something every developer eventually needs to understand:
Users, Permissions and Administrative Privileges.
We'll learn how file permissions work, how to execute commands with elevated privileges, and how Windows and Unix-like systems handle access control differently.
See you in Article 05!
