Command Chaining & Pipes in the Shell

Command Chaining & Pipes in the Shell

Command chaining and pipes are foundational techniques for working effectively in Unix-like shells such as Bash, or Zsh. They allow you to combine simple commands into useful workflows—something especially useful in DevOps, SRE, and automation contexts.  This guide walks through how these operators work, when to use them, and how to apply them in real-world scenarios.

Running Commands Sequentially (;)

The semicolon (;) lets you run multiple commands one after another, regardless of whether the previous command succeeds or fails.

Example

mkdir project ; cd project ; echo "initialized"        

Behavior

  • Each command runs in order
  • Failures do NOT stop execution

Use Case

Useful when you want a fixed sequence of steps and do not care about intermediate failures.

DevOps Example

docker stop myapp ; docker rm myapp ; docker run -d myapp:latest        

Even if docker stop fails (container not running), the rest still executes.

Conditional Execution on Success (&&)

The && operator runs the next command only if the previous command succeeds (exit code 0).

Example

mkdir project && cd project        

Behavior

  • Stops execution if a command fails
  • Ensures safe progression

Use Case

Ideal for setup scripts, deployment pipelines, and any operation that depends on success.

DevOps Example

git pull && docker build -t myapp . && docker run -d myapp        

If git pull fails, nothing else runs—preventing bad builds.

Conditional Execution on Failure (||)

The || operator runs the next command only if the previous command fails (non-zero exit code).

Example

mkdir project || echo "Directory already exists"        

Behavior

Acts as a fallback or error handler

Use Case

Great for scenarios that require graceful error handling, as well as implementing logging or fallback logic when operations do not proceed as expected.

DevOps Example

curl -f http://service/health || echo "Service is down"        

Combining && and ||

You can combine both operators to create compact control flow.

Example

mkdir project && echo "Created" || echo "Failed"        

Important Note

This behaves like:

if mkdir project; then
  echo "Created"
else
  echo "Failed"
fi        

Pipes (|) – Passing Output Between Commands

The pipe (|) sends the output of one command as input to another.

Basic Syntax

command1 | command2        

Example: Paging Output

ls /bin | less        

ls lists files, and less lets you scroll through them.

Example: Limiting Output

ls /bin | head -n 5        

Shows only the first 5 results

Example: Filtering

ps aux | grep nginx        

Lists processes and filters for nginx

Example: Counting Results

ls /var/log | wc -l        

Counts number of files

Advanced Pipe Workflows

Pipes become more useful when chaining multiple commands.

Example: Log Analysis

cat access.log | grep 500 | awk '{print $1}' | sort | uniq -c | sort -nr        

Breakdown

  1. cat → read log file
  2. grep 500 → filter errors
  3. awk → extract IP
  4. sort → prepare for counting
  5. uniq -c → count occurrences
  6. sort -nr → rank by frequency

Example: Kubernetes Debugging

kubectl get pods -A | grep CrashLoopBackOff | awk '{print $2}'        

Line Continuation (\)

The backslash (\) allows you to split long commands across multiple lines.

Example

mkdir project || \
echo "Directory exists"        

Multi-Line Pipeline Example

cat access.log | \
grep "ERROR" | \
awk '{print $2, $5}' | \
sort | uniq -c        

Real-World DevOps Patterns

Pattern 1: Safe Directory Setup

mkdir -p /app/data && cd /app/data        

Pattern 2: Build + Deploy Pipeline

npm install && npm run build && docker build -t app . && docker run -d app        

Pattern 3: Health Check with Fallback

curl -f http://localhost:8080/health || systemctl restart app        

Pattern 4: Log Monitoring Pipeline

tail -f /var/log/syslog | grep --line-buffered "ERROR"        

Pattern 5: Disk Usage Analysis

du -sh * | sort -hr | head -n 10        

Exit Codes and Why They Matter

All chaining relies on exit codes:

  • 0 → success
  • non-zero → failure

You can inspect it:

echo $?        

Example:

false && echo "won't run"
false || echo "runs because previous failed"        

Best Practices

Prefer && for critical workflows: Prevents cascading failures

Use || for fallback logic: Handle errors cleanly

Combine pipes with filters: Use grep, awk, sed, sort, uniq

Break long commands with \: Improves readability and maintainability

Avoid overly complex one-liners

Sometimes scripts are clearer:

#!/bin/bash
if mkdir project; then
  cd project
else
  echo "Failed"
fi        

Putting It All Together

Example: End-to-End Workflow

mkdir app && cd app && \
git clone https://github.com/example/repo . && \
docker build -t app . && \
docker run -d -p 8080:80 app || \
echo "Deployment failed"        

Final Thoughts

Command chaining and pipes are fundamentally about composition—taking small, focused commands and connecting them into reliable, efficient workflows that amplify their individual capabilities. By mastering these patterns, you enable faster troubleshooting through quick command combinations, cleaner automation by reducing unnecessary complexity, more powerful command-line usage that leverages existing tools effectively, and a reduced need for large, hard-to-maintain scripts. This approach is especially valuable in SRE and DevOps environments, where repeatability, reliability, and speed are critical, allowing engineers to build robust operational workflows using simple, composable building blocks.

To view or add a comment, sign in

More articles by Christopher Adamson

  • Advanced Linux Commands

    Advanced Linux usage goes far beyond running a few common commands to list files or manage processes. At an advanced…

  • Advanced SQL: Performance, Analytics, and Real-World Patterns

    This guide dives into advanced SQL concepts that matter in real systems—performance tuning, indexing strategy…

  • SQL from Zero to Confident

    SQL becomes much easier when you stop thinking of it as a language to memorize and start seeing it as a data pipeline:…

  • Accessible Loading States in HTMX with aria-busy

    One important note up front: current htmx documentation does not list an official extension literally named aria-busy…

  • The htmx response-targets Extension

    The response-targets extension solves a very practical problem in htmx: routing different HTTP responses to different…

  • Getting Started with the htmx preload Extension

    The preload extension in htmx is designed to make navigation and fragment loading feel faster by fetching content…

  • Debouncing Requests in htmx

    Modern interactive interfaces often rely on continuous user input, whether it’s searching, filtering, validating, or…

  • Caching Strategies in htmx

    When people talk about “the htmx caching extension,” they usually mean one of three different things: preload, which…

  • HTMX formdata extension: make formdata-aware components work with htmx

    Many modern UI components, including Web Components, custom inputs, and rating widgets, integrate with form submission…

    1 Comment
  • Htmx CSRF Extension (a.k.a. csrf-token)

    In plain htmx setups, CSRF protection is typically handled by adding a header globally with hx-headers or by relying on…

    1 Comment

Explore content categories