Why Shell Scripting?
Shell scripts automate command-line tasks:
- Batch operations
- System administration
- Build processes
- Scheduled tasks
As an agent, you'll use shell commands constantly.
Script Basics
Creating a Script
#!/bin/bash
# This is a comment
echo "Hello, World!"
Running a Script
# Make executable
chmod +x script.sh
# Run it
./script.sh
# Or run with bash
bash script.sh
Variables
Setting Variables
name="World"
count=42
path="/home/user"
# No spaces around =
name = "Wrong" # ERROR
name="Right" # OK
Using Variables
echo "Hello, $name"
echo "Count is ${count}"
echo "Path: $path/subdir"
Special Variables
$0 # Script name
$1 # First argument
$2 # Second argument
$@ # All arguments
$# # Number of arguments
$? # Exit status of last command
$ # Current process ID
Command Substitution
# Store command output in variable
today=$(date +%Y-%m-%d)
files=$(ls -1 | wc -l)
Conditionals
If Statements
if [ condition ]; then
# commands
elif [ other_condition ]; then
# commands
else
# commands
fi
Test Conditions
# String comparisons
[ "$a" = "$b" ] # Equal
[ "$a" != "$b" ] # Not equal
[ -z "$a" ] # Empty
[ -n "$a" ] # Not empty
# Number comparisons
[ "$a" -eq "$b" ] # Equal
[ "$a" -ne "$b" ] # Not equal
[ "$a" -lt "$b" ] # Less than
[ "$a" -gt "$b" ] # Greater than
# File tests
[ -f "$file" ] # Is regular file
[ -d "$dir" ] # Is directory
[ -e "$path" ] # Exists
[ -r "$file" ] # Is readable
[ -w "$file" ] # Is writable
[ -x "$file" ] # Is executable
Examples
# Check if file exists
if [ -f "config.yaml" ]; then
echo "Config found"
else
echo "Config missing"
fi
# Check argument count
if [ $# -lt 1 ]; then
echo "Usage: $0 <filename>"
exit 1
fi
Loops
For Loop
# Loop over list
for item in apple banana cherry; do
echo "$item"
done
# Loop over files
for file in *.txt; do
echo "Processing $file"
done
# Loop over range
for i in {1..5}; do
echo "Number $i"
done
# C-style for loop
for ((i=0; i<5; i++)); do
echo "Index $i"
done
While Loop
count=0
while [ $count -lt 5 ]; do
echo "Count: $count"
((count++))
done
Reading Lines
# Read file line by line
while read -r line; do
echo "Line: $line"
done < file.txt
# Read from command
ls -1 | while read -r file; do
echo "File: $file"
done
Functions
Defining Functions
# Function definition
greet() {
echo "Hello, $1!"
}
# With local variables
calculate() {
local a=$1
local b=$2
echo $((a + b))
}
Using Functions
greet "World"
# Output: Hello, World!
result=$(calculate 5 3)
echo "Result: $result"
# Output: Result: 8
Return Values
is_even() {
if [ $(($1 % 2)) -eq 0 ]; then
return 0 # True/success
else
return 1 # False/failure
fi
}
if is_even 4; then
echo "4 is even"
fi
Common Patterns
Error Handling
# Exit on error
set -e
# Check command success
if ! command; then
echo "Command failed"
exit 1
fi
# Or with ||
command || { echo "Failed"; exit 1; }
Default Values
# Use default if variable is empty
name=${1:-"default"}
port=${PORT:-8080}
Reading Input
echo "Enter your name:"
read -r name
echo "Hello, $name!"
# With prompt
read -p "Enter value: " value
Processing Arguments
while getopts "hf:v" opt; do
case $opt in
h) show_help; exit 0 ;;
f) file="$OPTARG" ;;
v) verbose=true ;;
*) echo "Unknown option"; exit 1 ;;
esac
done
Text Processing
Pipes and Filters
# Chain commands
cat file.txt | grep "pattern" | sort | uniq
# Count lines
wc -l < file.txt
# Extract columns
awk '{print $2}' file.txt
# Replace text
sed 's/old/new/g' file.txt
String Operations
str="Hello, World!"
# Length
echo ${#str} # 13
# Substring
echo ${str:0:5} # Hello
# Replace
echo ${str/World/Bash} # Hello, Bash!
# Remove prefix
file="document.txt"
echo ${file%.txt} # document
# Remove suffix
path="/home/user/file"
echo ${path##*/} # file
Practical Scripts
Backup Script
#!/bin/bash
set -e
src="/data"
dest="/backups"
date=$(date +%Y%m%d)
tar -czf "$dest/backup-$date.tar.gz" "$src"
echo "Backup complete: backup-$date.tar.gz"
Log Rotation
#!/bin/bash
log_dir="/var/log/myapp"
keep_days=7
find "$log_dir" -name "*.log" -mtime +$keep_days -delete
echo "Cleaned logs older than $keep_days days"
Health Check
#!/bin/bash
url="http://localhost:8080/health"
if curl -s "$url" | grep -q "ok"; then
echo "Service is healthy"
exit 0
else
echo "Service is unhealthy"
exit 1
fi
Batch Processing
#!/bin/bash
for file in input/*.csv; do
name=$(basename "$file" .csv)
./process.py "$file" > "output/${name}.json"
echo "Processed: $name"
done
Best Practices
Use Strict Mode
#!/bin/bash
set -euo pipefail
-e: Exit on error-u: Error on undefined variables-o pipefail: Pipe fails if any command fails
Quote Variables
# Bad - breaks with spaces
file=$1
cat $file
# Good
file="$1"
cat "$file"
Use Functions
Break scripts into functions for clarity and reuse.
Add Help
show_help() {
echo "Usage: $0 [options] <file>"
echo "Options:"
echo " -h Show help"
echo " -v Verbose output"
}
Log Actions
log() {
echo "[$(date +%H:%M:%S)] $*"
}
log "Starting process"
Conclusion
Shell scripting is essential for automation:
- Variables and substitution
- Conditionals and loops
- Functions for organization
- Pipes for text processing
Start with simple scripts, add complexity as needed.
Next: Docker Basics - Container fundamentals