Table of Contents
- What is a Shell Script?
- Prerequisites
- Your First Shell Script
- Fundamental Concepts
- Usage Methods
- Common Practices
- Best Practices
- Troubleshooting
- Conclusion
- References
What is a Shell Script?
A shell script is a text file containing a sequence of commands executed by a Unix shell (e.g., Bash, Zsh, or Dash). It acts as a “recipe” for the shell, automating tasks like file management, system monitoring, or data processing.
Most Linux/macOS systems use Bash (Bourne Again Shell) as the default shell, so we’ll focus on Bash scripting here. Scripts are saved with a .sh extension (convention, not requirement) and executed directly from the command line.
Prerequisites
Before diving in, ensure you have:
- Access to a Unix-like terminal (Linux, macOS, or WSL on Windows).
- Basic familiarity with command-line tools (
ls,cd,mkdir,rm, etc.). - A text editor (VS Code, Vim, Nano, or Sublime Text).
Your First Shell Script
Let’s write a simple “Hello World” script to get started. Follow these steps:
Step 1: Create the Script File
Open your text editor and create a new file named hello_world.sh.
Step 2: Add the Shebang Line
Every shell script should start with a shebang (#!), which tells the system which shell to use to execute the script. For Bash, use:
#!/bin/bash
Step 3: Add Commands
Below the shebang, add a comment (starts with #) and a command to print text:
#!/bin/bash
# My first shell script
echo "Hello, World!"
Step 4: Make It Executable
Scripts need execute permissions to run. Use chmod to grant this:
chmod +x hello_world.sh
Step 5: Run the Script
Execute the script with:
./hello_world.sh
Output:
Hello, World!
Congratulations! You’ve written and run your first shell script.
Fundamental Concepts
Variables
Variables store data for reuse. Use VAR_NAME=value to declare (no spaces around =), and $VAR_NAME to access.
Example:
#!/bin/bash
NAME="Alice"
AGE=30
echo "Name: $NAME" # Output: Name: Alice
echo "Age: $AGE" # Output: Age: 30
Quoting Variables: Always quote variables with "$VAR" to handle spaces in values:
FILE="my document.txt"
echo "Opening $FILE" # Good: Handles spaces
# echo "Opening $FILE" → "Opening my document.txt"
# Bad: Without quotes, the shell splits "my" and "document.txt" into separate args
# echo Opening $FILE → "Opening my document.txt" (accidentally splits)
Input/Output
Reading Input
-
Command-Line Arguments: Access with
$1,$2, …,$@(all args),$#(number of args).
Example:./script.sh apple banana→$1=apple,$2=banana. -
User Input: Use
readto capture input from the user:echo "Enter your name:" read -r NAME # -r prevents backslash escapes echo "Hello, $NAME!"
Writing Output
echo: Simple text output (supports escape sequences with-e).
Example:echo -e "Line 1\nLine 2"(newlines).printf: More control over formatting (like C’sprintf).
Example:printf "Name: %s, Age: %d\n" "$NAME" "$AGE".
Control Structures
If-Else
Check conditions (e.g., file existence, numeric comparisons). Use [ condition ] (POSIX) or [[ condition ]] (Bash-specific, supports regex).
Example: Check if a file exists
FILE="data.txt"
if [ -f "$FILE" ]; then # -f: regular file exists
echo "$FILE exists."
elif [ -d "$FILE" ]; then # -d: directory exists
echo "$FILE is a directory."
else
echo "$FILE does not exist."
fi
Loops
-
For Loop: Iterate over items (files, lists).
Example: Loop through.txtfiles:for file in *.txt; do echo "Processing $file" done -
While Loop: Run until a condition fails.
Example: Read lines from a file:while IFS= read -r line; do # IFS= preserves whitespace echo "Line: $line" done < "input.txt" # Redirect file into loop
Functions
Reusable code blocks. Define with function_name() { ... } and call with function_name.
Example:
greet() {
local name=$1 # Local variable (only in function)
echo "Hello, $name!"
}
greet "Bob" # Output: Hello, Bob!
greet "Charlie" # Output: Hello, Charlie!
Usage Methods
Running Scripts
- Direct Execution:
./script.sh(requires execute permission). - Via Shell:
bash script.shorsh script.sh(no execute permission needed). - Add to PATH: Move scripts to a directory in
$PATH(e.g.,~/bin) to run from anywhere:mv script.sh ~/bin/ # ~/bin must be in PATH (check with `echo $PATH`) script.sh # Now runs globally
Scheduling with Cron
Automate scripts to run at specific times using cron. Edit crontab with crontab -e and add a line like:
# Run backup script daily at 3 AM
0 3 * * * /home/user/scripts/backup.sh
Common Practices
- Comments: Explain “why” not “what” (e.g.,
# Backup logs before rotation). - Naming: Use
snake_casefor scripts/functions (e.g.,file_cleanup.sh). - Modularity: Split logic into functions (e.g.,
backup_files(),send_email()). - Error Handling: Check if commands succeed with
if command; then ...or useset -eto exit on error:set -e # Exit script if any command fails rm "important.txt" # Script exits if this fails (e.g., file not found)
Best Practices
Use Explicit Shebangs
Always specify #!/bin/bash (not #!/bin/sh, which may use a minimal shell like Dash).
Strict Mode
Enable set -euo pipefail for robust scripts:
-e: Exit on any command failure.-u: Treat undefined variables as errors.-o pipefail: Exit if any command in a pipeline fails.
#!/bin/bash
set -euo pipefail # Strict mode
Quote Variables
As mentioned earlier, quote variables to handle spaces: rm "$FILE" (not rm $FILE).
Avoid Hardcoded Paths
Use relative paths or environment variables (e.g., $HOME instead of /home/user).
Test with ShellCheck
Use ShellCheck to detect syntax errors and bad practices:
shellcheck script.sh # Install with `sudo apt install shellcheck` (Linux)
Document with Help Messages
Add a --help flag to explain usage:
if [ "$1" = "--help" ]; then
echo "Usage: $0 [name]"
echo "Prints a greeting to [name]."
exit 0
fi
Troubleshooting
- Permission Denied: Run
chmod +x script.shto fix execute permissions. - Syntax Error: Check for missing spaces (e.g.,
if[ ... ]→if [ ... ]) or useshellcheck. - Variable Not Expanding: Ensure variables are quoted (
"$VAR") and not in single quotes ('$VAR'). - Command Not Found: Check if dependencies are installed (e.g.,
jq,curl) or paths are correct.
Conclusion
Shell scripting is a cornerstone of Unix/Linux automation, enabling you to save time, reduce errors, and scale tasks. By mastering the fundamentals—variables, control structures, functions—and adopting best practices like strict mode and quoting, you can write robust, maintainable scripts.
Start small (e.g., a file cleanup script), test with shellcheck, and gradually tackle more complex projects (e.g., backups, log analysis). The Unix shell is a powerful ally—embrace it!
References
- Bash Reference Manual
- ShellCheck
- GNU Coreutils (docs for
ls,cp, etc.) - Learning the Bash Shell by Cameron Newham (O’Reilly Media)