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:

  1. ls -la lists directory contents.
  2. The pipe (|) takes its output.
  3. grep "report" searches that output.
  4. 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-ChildItem retrieves directory items.
  • Where-Object filters them.
  • $_ represents the current object being processed.
  • .Name accesses 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:

  • dir lists directory contents.
  • findstr searches 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:

  1. Redirect Standard Output to output.txt.
  2. 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:

  • 0 means 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!