| name | shell-scripting |
| description | Create, debug, and optimize shell scripts (bash, zsh, sh) for automation, system administration, file processing, and CLI workflows. Use when writing shell scripts, automating tasks, processing files, system administration, or working with command-line tools. |
Shell Scripting Skill
This skill provides expertise in creating, debugging, and optimizing shell scripts for automation, system administration, and CLI workflows.
Overview
Shell scripting enables automation of:
- File operations and batch processing
- System administration tasks
- Command-line tool orchestration
- Text processing and data manipulation
- Workflow automation
- CI/CD pipeline scripts
Shell Types
Bash (Bourne Again Shell)
- Most common on Linux and macOS
- Default on many systems
- Extensive features and compatibility
Zsh (Z Shell)
- Default on macOS (since Catalina)
- Enhanced features over bash
- Better tab completion and globbing
Sh (POSIX Shell)
- Most portable
- Minimal features
- Best for maximum compatibility
Basic Script Structure
#!/bin/bash
set -e
set -u
set -o pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SCRIPT_NAME="$(basename "$0")"
function usage() {
echo "Usage: $SCRIPT_NAME [options]"
}
main() {
echo "Script execution starts here"
}
main "$@"
Variables and Data Types
Variable Assignment
NAME="value"
NUMBER=42
CURRENT_DIR=$(pwd)
DATE=$(date +%Y-%m-%d)
FILE_COUNT=$(ls -1 | wc -l)
readonly CONSTANT="immutable value"
export ENV_VAR="value"
Arrays
declare -a ARRAY=("item1" "item2" "item3")
echo "${ARRAY[0]}"
echo "${ARRAY[@]}"
echo "${#ARRAY[@]}"
ARRAY+=("item4")
for item in "${ARRAY[@]}"; do
echo "$item"
done
Associative Arrays (Bash 4+)
declare -A DICT
DICT["key1"]="value1"
DICT["key2"]="value2"
echo "${DICT[key1]}"
Control Flow
Conditionals
if [ condition ]; then
commands
elif [ condition ]; then
commands
else
commands
fi
[ -f file ]
[ -d dir ]
[ -r file ]
[ -w file ]
[ -x file ]
[ -z string ]
[ -n string ]
[ str1 = str2 ]
[ str1 != str2 ]
[ num1 -eq num2 ]
[ num1 -gt num2 ]
Loops
for i in {1..10}; do
echo "$i"
done
for file in *.txt; do
echo "Processing $file"
done
while [ condition ]; do
commands
done
until [ condition ]; do
commands
done
for ((i=0; i<10; i++)); do
echo "$i"
done
Functions
function my_function() {
local var1="$1"
local var2="$2"
echo "Processing $var1 and $var2"
return 0
}
my_function "arg1" "arg2"
if my_function "arg1" "arg2"; then
echo "Function succeeded"
fi
Input/Output
Reading Input
read -p "Enter name: " name
read -s -p "Password: " password
while IFS= read -r line; do
echo "$line"
done < file.txt
SCRIPT_NAME="$0"
FIRST_ARG="$1"
ALL_ARGS="$@"
ARG_COUNT="$#"
Output
echo "Text"
echo -e "Text with\nnewline"
echo -n "No newline"
printf "Name: %s, Age: %d\n" "$name" "$age"
command > file.txt
command >> file.txt
command 2> error.log
command &> all.log
Text Processing
sed (Stream Editor)
sed 's/old/new/g' file.txt
sed '/pattern/d' file.txt
sed -n '10,20p' file.txt
awk
awk '{print $1, $3}' file.txt
awk '$3 > 100 {print $1, $3}' file.txt
awk -F',' '{print $1}' file.csv
grep
grep "pattern" file.txt
grep -r "pattern" directory/
grep -i "pattern" file.txt
grep -v "pattern" file.txt
grep -E "regex" file.txt
File Operations
File Tests
[ -f file ]
[ -d dir ]
[ -L link ]
[ -r file ]
[ -w file ]
[ -x file ]
[ -s file ]
[ -e path ]
File Manipulation
cp source.txt dest.txt
cp -r source_dir/ dest_dir/
mv old.txt new.txt
rm file.txt
rm -rf directory/
touch file.txt
mkdir -p path/to/dir
cat file.txt
head -n 10 file.txt
tail -n 10 file.txt
less file.txt
Process Management
Running Commands
command &
wait $PID
(cd /tmp && ls)
RESULT=$(command)
RESULT=`command`
Process Control
PID=$$
kill $PID
kill -9 $PID
killall process_name
if pgrep -f "pattern"; then
echo "Process is running"
fi
Error Handling
set -e
set -u
set -o pipefail
trap 'echo "Error on line $LINENO"' ERR
trap 'rm -f /tmp/tempfile' EXIT
if ! command; then
echo "Command failed"
exit 1
fi
Best Practices
- Always use shebang:
#!/bin/bash or #!/usr/bin/env bash
- Set error handling:
set -euo pipefail
- Quote variables:
"$var" not $var
- Use local variables:
local var="value" in functions
- Validate input: Check arguments and file existence
- Use meaningful names: Clear variable and function names
- Add comments: Document complex logic
- Handle errors: Check return codes and handle failures
- Use functions: Break code into reusable functions
- Test incrementally: Test as you build
Common Patterns
Script Template
#!/usr/bin/env bash
set -euo pipefail
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly SCRIPT_NAME="$(basename "$0")"
function usage() {
cat << EOF
Usage: $SCRIPT_NAME [OPTIONS]
Description of what the script does.
OPTIONS:
-h, --help Show this help message
-v, --verbose Enable verbose output
-f, --file FILE Specify input file
EXAMPLES:
$SCRIPT_NAME -f input.txt
$SCRIPT_NAME --verbose
EOF
}
function log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*" >&2
}
function main() {
local verbose=false
local input_file=""
while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help)
usage
exit 0
;;
-v|--verbose)
verbose=true
shift
;;
-f|--file)
input_file="$2"
shift 2
;;
*)
echo "Unknown option: $1" >&2
usage
exit 1
;;
esac
done
[[ -z "$input_file" ]] && { echo "Error: Input file required" >&2; exit 1; }
[[ ! -f "$input_file" ]] && { echo "Error: File not found: $input_file" >&2; exit 1; }
log "Processing $input_file"
}
main "$@"
Logging Function
function log() {
local level="$1"
shift
local message="$*"
local timestamp=$(date +'%Y-%m-%d %H:%M:%S')
case "$level" in
INFO)
echo "[$timestamp] [INFO] $message" >&2
;;
WARN)
echo "[$timestamp] [WARN] $message" >&2
;;
ERROR)
echo "[$timestamp] [ERROR] $message" >&2
;;
esac
}
log INFO "Script started"
log WARN "This is a warning"
log ERROR "This is an error"
Resources