Table of Contents
- What is a Linux Shell?
- Getting Started with Shell Scripting
- Variables in Shell Scripting
- Input/Output Handling
- Control Structures: Conditionals and Loops
- Functions in Shell Scripts
- Arrays: Managing Collections of Data
- Error Handling and Debugging
- Advanced Topics
- Best Practices for Writing Shell Scripts
- Real-World Examples
- References
What is a Linux Shell?
The shell is a command-line interpreter that acts as an interface between the user and the Linux kernel. It reads user input (commands), executes them, and returns output. While graphical user interfaces (GUIs) are user-friendly, the shell offers unparalleled control and automation capabilities.
Common Shells
- Bash (Bourne-Again SHell): The most popular shell, default on most Linux distributions (Ubuntu, Fedora, Debian). It extends the original Bourne Shell (sh) with features like command history, tab completion, and scripting support.
- Zsh (Z Shell): A modern shell with enhanced features (themes, plugins, better globbing) popular among developers.
- Fish (Friendly Interactive SHell): Designed for simplicity and user-friendliness, with auto-suggestions and syntax highlighting.
- Ksh (Korn Shell): Combines features of Bash and C Shell (csh), used in some enterprise environments.
For this blog, we’ll focus on Bash, as it’s the de facto standard for scripting.
Getting Started with Shell Scripting
1. Shebang Line
Every shell script starts with a shebang line (#!), which tells the system which interpreter to use. For Bash scripts, this is:
#!/bin/bash
Save the script with a .sh extension (e.g., script.sh).
2. Writing Your First Script
Let’s create a simple “Hello World” script:
#!/bin/bash
# This is a comment (ignored by the shell)
echo "Hello, World!" # Print text to the console
3. Executing the Script
To run the script:
- Make it executable with
chmod +x script.sh(sets the executable permission). - Execute it with
./script.sh(the./ensures the current directory is searched).
Alternatively, run it directly with the Bash interpreter: bash script.sh (no need for chmod in this case).
Variables in Shell Scripting
Variables store data for reuse. Bash supports two types of variables: local variables (visible only in the script) and environment variables (system-wide, inherited by child processes).
1. Declaring Variables
- No spaces around the
=sign. - Variable names: Start with a letter/underscore, followed by letters, numbers, or underscores.
name="Alice" # String variable
age=30 # Numeric variable
2. Accessing Variables
Use $variable_name to access a variable’s value:
echo "Name: $name" # Output: Name: Alice
echo "Age: $age" # Output: Age: 30
3. Environment Variables
These are predefined by the system (or set by the user) and control system behavior. Examples:
PATH: Directories searched for executable commands.HOME: User’s home directory (e.g.,/home/alice).USER: Current username.PWD: Current working directory.
Access them like local variables:
echo "Home: $HOME" # Output: Home: /home/alice
echo "PATH: $PATH" # Output: List of directories
4. Setting Environment Variables
Use export to make a local variable an environment variable:
export MY_VAR="Hello" # Now accessible to child processes
Input/Output Handling
Shell scripts interact with users and files through input/output (I/O) operations.
1. Output: echo and printf
-
echo: Prints text to the console. Use-eto enable escape sequences (\nfor newline,\tfor tab):echo "Hello\nWorld" # Output: Hello\nWorld (no escape) echo -e "Hello\nWorld" # Output: Hello (newline) World -
printf: For formatted output (similar to C’sprintf). Supports placeholders like%s(string),%d(integer),%f(float):printf "Name: %s, Age: %d\n" "Alice" 30 # Output: Name: Alice, Age: 30
2. Input: read Command
Use read to capture user input. Add -p to display a prompt:
#!/bin/bash
read -p "Enter your name: " name # Prompt user for input
echo "Hello, $name!" # Output: Hello, [name]!
3. Redirection
Redirect output to files or input from files using operators:
>: Overwrite a file (e.g.,echo "Hi" > file.txt).>>: Append to a file (e.g.,echo "Again" >> file.txt).<: Read input from a file (e.g.,sort < file.txt).2>: Redirect errors to a file (e.g.,command 2> errors.log).&>: Redirect both output and errors (e.g.,command &> output.log).
4. Pipes (|)
Chain commands with | to pass output of one command as input to another:
ls -l | grep ".sh" # List all .sh files (ls output → grep input)
Control Structures: Conditionals and Loops
Control structures let you execute code conditionally or repeatedly.
1. If-Else Statements
Check conditions and execute code blocks accordingly.
Syntax:
if [ condition ]; then
# Code if condition is true
elif [ another_condition ]; then
# Code if first condition is false, second is true
else
# Code if all conditions are false
fi
Conditions:
- Numeric:
-eq(equal),-ne(not equal),-lt(less than),-gt(greater than). - String:
=,!=(equality/inequality),-z(empty string),-n(non-empty string). - File:
-f(file exists),-d(directory exists),-x(executable).
Example: Check if a file exists:
#!/bin/bash
file="data.txt"
if [ -f "$file" ]; then
echo "$file exists."
else
echo "$file does NOT exist."
fi
2. Loops
For Loop
Iterate over a list of items (files, numbers, strings):
#!/bin/bash
# Loop through a list of fruits
fruits=("apple" "banana" "cherry")
for fruit in "${fruits[@]}"; do
echo "I like $fruit"
done
While Loop
Run code as long as a condition is true:
#!/bin/bash
count=5
while [ $count -gt 0 ]; do
echo "Countdown: $count"
count=$((count - 1)) # Decrement count
sleep 1 # Wait 1 second
done
echo "Blast off!"
Until Loop
Run code until a condition is true (opposite of while):
#!/bin/bash
count=1
until [ $count -gt 5 ]; do
echo "Count: $count"
count=$((count + 1))
done
Functions in Shell Scripts
Functions group reusable code into modular blocks.
1. Defining a Function
Syntax:
function_name() {
# Code here
local local_var="I'm local" # Local variable (scope limited to function)
echo "$local_var"
}
2. Parameters and Return Values
- Access parameters with
$1,$2, … (first, second argument). - Return values via
return(exit code, 0-255) or output (useechoand capture with$()).
Example:
#!/bin/bash
greet() {
local name=$1 # First parameter
echo "Hello, $name!"
return 0 # Success exit code
}
greet "Bob" # Output: Hello, Bob!
Arrays: Managing Collections of Data
Arrays store multiple values in a single variable.
1. Declaring Arrays
# Method 1: Explicit declaration
fruits=("apple" "banana" "cherry")
# Method 2: Indexed assignment
fruits[0]="apple"
fruits[1]="banana"
2. Accessing Elements
${array[index]}: Access element atindex(starts at 0).${array[@]}: All elements.${#array[@]}: Number of elements.
Example:
echo "${fruits[1]}" # Output: banana
echo "${fruits[@]}" # Output: apple banana cherry
echo "${#fruits[@]}" # Output: 3
3. Looping Through Arrays
for fruit in "${fruits[@]}"; do
echo "$fruit"
done
Error Handling and Debugging
1. Exit Codes
Every command returns an exit code (0 = success, non-zero = failure). Check with $?:
ls non_existent_file
echo "Exit code: $?" # Output: 2 (failure)
2. set Options
set -e: Exit the script if any command fails.set -u: Treat undefined variables as errors.set -o pipefail: Exit if any command in a pipe fails.
Add these at the top of your script for strict error checking:
#!/bin/bash
set -euo pipefail # Exit on errors, undefined vars, or pipe failures
3. Debugging
bash -x script.sh: Run the script in debug mode (prints each command before execution).set -xin the script: Enable debugging temporarily.set +x: Disable debugging.
Advanced Topics
1. Command Substitution
Capture output of a command into a variable with $() or backticks `:
current_date=$(date +%Y-%m-%d) # Get current date
echo "Today is $current_date" # Output: Today is 2024-05-20
2. Here Documents
Embed multi-line text directly in the script with <<EOF (end with EOF):
cat <<EOF > report.txt
Report generated on: $(date)
User: $USER
EOF
3. Traps
Catch signals (e.g., Ctrl+C) or script exit to clean up resources:
#!/bin/bash
cleanup() {
echo "Cleaning up temp files..."
rm -f temp.txt
}
trap cleanup EXIT # Run cleanup when script exits
trap 'echo "Aborted!"; exit 1' SIGINT # Run on Ctrl+C
Best Practices for Writing Shell Scripts
- Shebang Line: Always start with
#!/bin/bash. - Comments: Explain complex logic (use
#). - Quoting: Use
"$var"to handle spaces in variables (e.g.,file="$1"instead offile=$1). - Error Checking: Validate inputs and command success (use
set -eorif ! command; then ...). - Modularity: Use functions for reusable code.
- Testing: Test scripts with different inputs and edge cases.
- Version Control: Store scripts in Git for tracking changes.
Real-World Examples
Example 1: Backup Script
Automatically back up a directory to a compressed file:
#!/bin/bash
set -euo pipefail
SOURCE_DIR="/home/user/documents"
BACKUP_DIR="/mnt/backup"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/docs_backup_$TIMESTAMP.tar.gz"
# Check if source exists
if [ ! -d "$SOURCE_DIR" ]; then
echo "Error: Source directory $SOURCE_DIR does not exist."
exit 1
fi
# Create backup
echo "Creating backup: $BACKUP_FILE"
tar -czf "$BACKUP_FILE" -C "$SOURCE_DIR" .
echo "Backup completed successfully!"
Example 2: System Info Script
Display CPU, memory, and disk usage:
#!/bin/bash
echo "=== System Info ==="
echo "CPU Usage: $(top -bn1 | grep "Cpu(s)" | awk '{print $2 + $4}')%"
echo "Memory Usage: $(free -m | awk '/Mem:/ {print $3 "MB / " $2 "MB"}')"
echo "Disk Usage: $(df -h / | awk '/\// {print $3 " / " $2}')"
References
- GNU Bash Manual
- Bash Guide for Beginners
- Advanced Bash-Scripting Guide
- Linux Man Pages (e.g.,
man bash,man read) - Book: Learning the Bash Shell by Cameron Newham (O’Reilly Media)
By mastering shell scripting, you’ll unlock the full potential of Linux automation. Start small, experiment, and gradually build more complex scripts—your future self (and sysadmin team) will thank you! 🚀