Shell Scripting Guide
CJ's Shell supports comprehensive shell scripting with POSIX compliance and bash-like extensions. This guide covers the essential scripting features.
Script Basics
Shebang
Start your scripts with a shebang to specify the interpreter:
Or for POSIX compatibility:
Making Scripts Executable
Running Scripts
# Execute in a subshell
cjsh script.sh
# Source in current shell (preserves variables and aliases)
source script.sh
. script.sh
Variables
Variable Assignment
Note: No spaces around the = sign.
Variable Expansion
Special Variables
$0 # Script name
$1-$9 # Positional parameters
$@ # All positional parameters (as separate words)
$* # All positional parameters (as single word)
$# # Number of positional parameters
$? # Exit status of last command
$$ # Process ID of current shell
$! # Process ID of last background command
Environment Variables
# Export to make available to child processes
export PATH="/usr/local/bin:$PATH"
export MY_VAR="value"
# Set and export in one line
export NEW_VAR="value"
# Remove variable
unset MY_VAR
Local Variables (in functions)
Read-only Variables
Conditionals
if Statements
if [ condition ]; then
commands
fi
# With else
if [ condition ]; then
commands
else
other_commands
fi
# With elif
if [ condition1 ]; then
commands1
elif [ condition2 ]; then
commands2
else
commands3
fi
Test Expressions
String Tests
[ -z "$str" ] # True if string is empty
[ -n "$str" ] # True if string is not empty
[ "$a" = "$b" ] # True if strings are equal
[ "$a" != "$b" ] # True if strings are not equal
Numeric Tests
[ "$a" -eq "$b" ] # Equal
[ "$a" -ne "$b" ] # Not equal
[ "$a" -lt "$b" ] # Less than
[ "$a" -le "$b" ] # Less than or equal
[ "$a" -gt "$b" ] # Greater than
[ "$a" -ge "$b" ] # Greater than or equal
File Tests
[ -e file ] # True if file exists
[ -f file ] # True if regular file
[ -d file ] # True if directory
[ -r file ] # True if readable
[ -w file ] # True if writable
[ -x file ] # True if executable
[ -s file ] # True if file size > 0
[ -L file ] # True if symbolic link
Logical Operators
[ condition1 ] && [ condition2 ] # AND
[ condition1 ] || [ condition2 ] # OR
[ ! condition ] # NOT
# Within test
[ condition1 -a condition2 ] # AND (deprecated, use && instead)
[ condition1 -o condition2 ] # OR (deprecated, use || instead)
Extended Test [[ ]]
Bash-style extended tests (not POSIX):
[[ $str =~ pattern ]] # Regex matching
[[ $str == pattern ]] # Pattern matching
[[ $a < $b ]] # String comparison
[[ condition1 && condition2 ]] # Logical AND
[[ condition1 || condition2 ]] # Logical OR
Loops
for Loop
# Iterate over list
for item in one two three; do
echo "$item"
done
# Iterate over files
for file in *.txt; do
echo "Processing $file"
done
# C-style for loop (bash extension)
for ((i=0; i<10; i++)); do
echo "$i"
done
while Loop
counter=0
while [ $counter -lt 10 ]; do
echo "$counter"
counter=$((counter + 1))
done
# Read file line by line
while IFS= read -r line; do
echo "$line"
done < file.txt
until Loop
Loop Control
# break - Exit loop
for i in 1 2 3 4 5; do
if [ $i -eq 3 ]; then
break
fi
echo "$i"
done
# continue - Skip to next iteration
for i in 1 2 3 4 5; do
if [ $i -eq 3 ]; then
continue
fi
echo "$i"
done
Functions
Function Definition
# Method 1 (POSIX)
function_name() {
commands
}
# Method 2 (bash-style)
function function_name() {
commands
}
Function Parameters
greet() {
local name="$1"
local greeting="${2:-Hello}" # Default value
echo "$greeting, $name!"
}
greet "Alice" # Hello, Alice!
greet "Bob" "Hi" # Hi, Bob!
Return Values
is_valid() {
if [ -f "$1" ]; then
return 0 # Success
else
return 1 # Failure
fi
}
# Use in condition
if is_valid "myfile.txt"; then
echo "File is valid"
fi
Capturing Output
Command Substitution
Modern Syntax
Nested Substitution
Arithmetic
Integer Arithmetic
# Using $(( ))
result=$((5 + 3))
sum=$((a + b))
product=$((a * b))
division=$((a / b))
modulo=$((a % b))
# Increment/Decrement
count=$((count + 1))
count=$((count - 1))
# Using expr (older style)
result=$(expr 5 + 3)
Common Operations
# Assignment with arithmetic
((count++))
((count--))
((count += 5))
((count *= 2))
# Comparisons in arithmetic context
if ((a > b)); then
echo "a is greater"
fi
Input/Output
Reading Input
# Read single variable
read name
echo "Hello, $name"
# Read multiple variables
read first last
echo "Name: $first $last"
# With prompt
read -p "Enter your name: " name
# Read into array
read -a array
echo "${array[0]}"
# Read with timeout (bash extension)
read -t 5 -p "Enter within 5 seconds: " input
Printing Output
# echo
echo "Hello, World"
echo -n "No newline"
echo -e "Enable\tescapes\nhere"
# printf (more portable)
printf "Name: %s\n" "$name"
printf "Number: %d\n" 42
printf "Float: %.2f\n" 3.14159
printf "%-10s %5d\n" "Item" 123
Redirection
Output Redirection
command > file # Redirect stdout, overwrite
command >> file # Redirect stdout, append
command 2> file # Redirect stderr
command &> file # Redirect both stdout and stderr
command 2>&1 # Redirect stderr to stdout
command > file 2>&1 # Redirect both to file
Input Redirection
command < file # Read from file
command << EOF # Here-document
multi-line
input
EOF
command <<< "string" # Here-string
File Descriptors
exec 3< input.txt # Open file for reading
exec 4> output.txt # Open file for writing
read -u 3 line # Read from fd 3
echo "text" >&4 # Write to fd 4
exec 3<&- # Close fd 3
exec 4>&- # Close fd 4
Pipelines
Basic Pipelines
Pipeline Status
# Exit status is from last command
ls | grep "pattern"
echo $? # Status from grep
# Get status of all pipeline commands
ls | grep "pattern"
echo "${PIPESTATUS[@]}" # bash extension
Case Statements
case "$variable" in
pattern1)
commands1
;;
pattern2|pattern3)
commands2
;;
*)
default_commands
;;
esac
Example
case "$1" in
start)
echo "Starting..."
;;
stop)
echo "Stopping..."
;;
restart)
echo "Restarting..."
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
;;
esac
Error Handling
Exit on Error
set -e # Exit immediately if command fails
set -u # Exit if undefined variable is used
set -o pipefail # Pipeline fails if any command fails
# Combine
set -euo pipefail
Custom Error Handling
Trap for Cleanup
cleanup() {
echo "Cleaning up..."
rm -f temp_file
}
trap cleanup EXIT
trap 'echo "Interrupted"; exit 1' INT TERM
Arrays (bash extension)
Array Creation
Array Access
echo "${array[0]}" # First element
echo "${array[@]}" # All elements
echo "${#array[@]}" # Array length
echo "${array[*]}" # All elements as single word
Array Iteration
Best Practices
Quoting
# Always quote variables
echo "$variable"
command "$file_name"
# Quote to preserve whitespace
for file in "$@"; do
echo "$file"
done
Script Template
#!/usr/bin/env cjsh
# Script description
# Author: Your Name
# Date: YYYY-MM-DD
set -euo pipefail
# Constants
readonly SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
readonly SCRIPT_NAME="$(basename "$0")"
# Functions
usage() {
cat << EOF
Usage: $SCRIPT_NAME [options] arguments
Description of script
Options:
-h, --help Show this help message
-v, --verbose Enable verbose output
Examples:
$SCRIPT_NAME file.txt
EOF
}
main() {
# Main script logic
echo "Script started"
}
# Parse arguments
while [ $# -gt 0 ]; do
case "$1" in
-h|--help)
usage
exit 0
;;
-v|--verbose)
VERBOSE=1
shift
;;
*)
break
;;
esac
done
# Run main
main "$@"
Debugging
# Enable debugging output
set -x # Print commands before execution
# Debug specific section
set -x
commands_to_debug
set +x
# Use in script
#!/usr/bin/env cjsh -x
Validation
# Use syntax command to check script
cjsh syntax script.sh
# Check for specific commands
cjsh validate command_name
Common Patterns
Argument Parsing
while getopts "hvf:" opt; do
case "$opt" in
h) usage; exit 0 ;;
v) VERBOSE=1 ;;
f) FILE="$OPTARG" ;;
*) usage; exit 1 ;;
esac
done
shift $((OPTIND - 1))
File Processing
Temporary Files
Default Values
# Use default if variable is unset
name="${name:-default}"
# Set default if variable is unset
: "${name:=default}"
# Error if variable is unset
: "${name:?Error: name is required}"
Additional Resources
- See
helpcommand for built-in command reference - Use
man testfor detailed test expression documentation - Check POSIX specification for portable scripting
- Run
./tests/run_shell_tests.shto see example test scripts