software-development

Shell Scripting Basic Commands: A Practical Guide with Verified Examples

Shell scripting automates command-line tasks by chaining basic commands into files the shell can execute. This evergreen explainer covers universal concepts and commands that re...

Mara Ellison
Shell Scripting Basic Commands: A Practical Guide with Verified Examples

Overview and Core Principles of Shell Scripting

Shell scripting automates command-line tasks by chaining basic commands into files the shell can execute. This evergreen explainer covers universal concepts and commands that remain useful across Bash, POSIX sh, and common Linux and macOS workflows. You will learn when to use a script versus one-off commands, how commands exit, and how quoting and expansions behave. These fundamentals help you build reliable, maintainable automation without relying on external tools.

Essential Shell Commands for Everyday Scripts

Effective scripts combine a small set of reliable commands. Master these to handle the majority of automation safely and portably.

File and Directory Operations

Use commands that clearly express intent and avoid surprising behavior across environments. Prefer explicit options and absolute paths when scripts run from different working directories.

  • ls: List directory contents with predictable formatting (ls -1 for one entry per line).
  • cp: Copy files and directories (cp -p to preserve timestamps and permissions where supported).
  • mv: Move or rename files atomically within the same filesystem.
  • rm: Remove files and directories carefully (rm -f to avoid interactive prompts, rm -r for directories).
  • mkdir: Create directories (mkdir -p to create parent directories as needed).
  • test [ and [[ ]]: Evaluate file attributes and string/integer conditions (e.g., -f, -d, =, !=).

Process and I/O Control

  • echo: Emit text (use echo -e cautiously; prefer printf for portable formatted output).
  • printf: Format output with explicit format strings for reliable, readable logs.
  • pwd: Print the current working directory to anchor relative paths.
  • cd: Change directory; combine with pwd to validate context before operating.
  • sleep: Pause execution for a given number of seconds (sleep 1 for one second).
  • exit: End a script with an exit code (exit 0 for success, non-zero for errors).

Text and Stream Utilities

These standard utilities are widely available and compose cleanly via pipelines.

  • cat: Concatenate and output file content (avoid cat file | cmd; prefer cmd < file).
  • grep: Filter lines matching a pattern (grep -F for fixed strings, -x for full-line matches).
  • cut and awk: Extract columns or fields; awk is preferable for complex parsing.
  • sort and uniq: Order and deduplicate lines (sort -u for unique lines).
  • wc: Count lines, words, and bytes (wc -l for line count).

Core Scripting Constructs

Combine commands with shell syntax to create robust logic without overcomplicating your code.

Variables, Expansion, and Quoting

Use variables to parameterize scripts, but always quote expansions to prevent word splitting and globbing.

  • Assign and expand: name=value; echo "$name" (double quotes preserve spaces).
  • Parameter expansion: ${name-default} supplies a default when name is unset.
  • Export: export name to pass variables to child processes.
  • Positional parameters: $1, $2, …, ${10} for script arguments; $# for count; $0 for script name.

Conditionals and Exit Codes

Shell conditionals rely on exit codes; commands that succeed return 0, failures return non-zero.

  • if/then/else: if grep -q pattern file; then echo found; else echo not found; fi.
  • case: Match patterns cleanly when you have multiple discrete values.
  • Logical operators && and ||: Run commands conditionally (cmd1 && cmd2 runs cmd2 only if cmd1 succeeds).

Loops for Repetition

  • for item in a b c; do echo $item; done: Iterate over a list.
  • while read line; do echo "$line"; done < file: Process file lines safely with IFS= read -r.

Sample Verified Script and Useful Table

A concise, working example demonstrates how common commands integrate into a reliable script.

Example: Backup and Report Script

This script copies a single file to a timestamped backup location, logs actions, and exits with an appropriate code. It avoids interactive prompts and uses portable syntax.

#!/bin/sh
# backup_report.sh - Simple, portable backup example
src="$1"
if [ -z "$src" ]; then
  echo "Usage: $0 <file>"
  exit 2
fi
dst="backup_$(date +%Y%m%d_%H%M%S)_$(basename "$src")"
if cp -p -- "$src" "$dst"; then
  printf '%s: copied %s to %s\n' "$(date)" "$src" "$dst"
  exit 0
else
  printf '%s: failed to copy %s\n' "$(date)" "$src" >&2
  exit 1
fi
AttributeVerified DetailSource Type
Shebang portabilityUse #!/bin/sh for POSIX behavior; #!/bin/bash for bash-specific featuresImplementation convention
Quoting variablesAlways double-quote expansions ("$var") to prevent word splitting and globbingBest practice
Exit code conventions0 indicates success; non-zero indicates different error typesPOSIX standard
cp -p behaviorPreserves mode, ownership, and timestamps when supported by the filesystemPOSIX utility specification
grep -q usageQuiet mode; sets exit code based on match presence, produces no outputGNU coreutils, widely portable
read -r behavior-r prevents backslash interpretation, preserving literal contentPOSIX shell specification

Common Pitfalls and Safe Patterns

Avoiding these frequent issues makes scripts more predictable and portable.

  • Unquoted variables: ""$var"" prevents splitting and globbing.
  • Relying on ls parsable output: Prefer find -print0 or explicit globbing when possible.
  • Assuming paths are relative: Use pwd or dirname "$0" to anchor scripts run from different directories.
  • Ignoring exit codes: Check command success with if, &&, or || as appropriate.
  • Using non-portable extensions: Keep scripts #!/bin/sh unless you need bash-specific features.

Testing and Debugging Scripts

Validate behavior early and often to catch portability and logic issues.

  • Dry-run with echo: Temporarily replace destructive commands with echo to preview actions.
  • ShellCheck: Run static analysis to identify common defects and style issues.
  • Set -e and set -u: Consider set -euo pipefail for stricter error detection (review implications before enabling broadly).
  • Trace execution: Use sh -x script.sh to see each command expanded before execution.
  • Unit simple pieces: Test conditionals and expansions with small, standalone snippets before integrating.

Extending to Larger Workflows

As scripts grow, structure them for clarity and reuse without introducing heavy dependencies.

  • Use functions to group related commands and avoid duplication.
  • Log with timestamps and severity labels to aid troubleshooting.
  • Validate inputs and fail fast with clear usage messages.
  • Ensure signals and temporary files are handled in long-running processes.
  • Keep scripts small and composable; prefer pipelines of simple utilities over monolithic logic.

Summary and Best Practices

Shell scripting with basic commands remains a durable, efficient way to automate work on Unix-like systems. Write scripts that are explicit, portable, and defensive: quote expansions, check exit codes, prefer standard utilities, and validate assumptions with tests. By combining core commands, conditionals, loops, and careful quoting, you can build automation that is maintainable and reliable over time.

Related Reading

More pages in this topic cluster.

How to Make Minecraft Plugins: A Verified Technical Guide

Making a Minecraft plugin means writing server side code that hooks into the Minecraft server software to change or extend gameplay, commands, data, and integrations. Unlike mod...

Read next
Sprint Dirt: What It Is, Why It Happens, and How to Manage It

Sprint dirt is the accumulation of small, often invisible issues that slow teams down across a sprint—unclear requirements, brittle tests, flaky environments, and handoff fric...

Read next
Understanding Chandler Garbage Collection in Computing

In computing, garbage collection is an automatic memory management mechanism that reclaims unused objects to free resources. In the context of the Chandler information manager,...

Read next