sh - POSIX Shell
Die POSIX Shell (sh) stellt die standardisierte Basis der Unix Shell-Programmierung dar, die durch den IEEE POSIX.1 Standard definiert ist. Als die tragbare und weit verbreitete Shell über Unix-ähnliche Systeme bietet sh die wesentlichen Funktionen und Syntax, die die Basis für Shell-Skripting bilden. Während moderne Shells wie Bash und Zsh umfangreiche Verbesserungen bieten, ist das Verständnis von POSIX sh entscheidend für das Schreiben von tragbaren Skripten, die über verschiedene Systeme arbeiten, von eingebetteten Geräten bis zu Unternehmensservern. Die POSIX Shell sorgt für maximale Kompatibilität und dient als gemeinsamer Nenner für die Shell-Programmierung über verschiedene Unix-Umgebungen.
POSIX Shell Fundamentals
POSIX verstehen Einhaltung
```sh
Check if shell is POSIX compliant
echo $0 /bin/sh
Verify POSIX mode (if using bash as sh)
set -o posix
Check shell features
command -v command >/dev/null 2>&1 && echo "command builtin available" test -n "$\\{BASH_VERSION\\}" && echo "Running under Bash" test -n "$\\{ZSH_VERSION\\}" && echo "Running under Zsh" ```_
Grund Shell Invokation
```sh
Execute shell script
sh script.sh sh -x script.sh # Debug mode sh -n script.sh # Syntax check only sh -e script.sh # Exit on error sh -u script.sh # Exit on undefined variable
Interactive shell
sh -i # Force interactive mode sh -l # Login shell sh -s # Read from stdin
Combining options
sh -eux script.sh # Exit on error, undefined vars, debug ```_
Shell Optionen und Einstellungen
```sh
Set shell options
set -e # Exit on error set -u # Exit on undefined variable set -x # Debug mode (print commands) set -v # Verbose mode (print input) set -n # No execution (syntax check)
Unset options
set +e # Don't exit on error set +u # Allow undefined variables set +x # Disable debug mode
Check if option is set
case $- in e) echo "Exit on error is set" ;; *) echo "Exit on error is not set" ;; esac ```_
Variablen und Parametererweiterung
Variable Zuordnung und Nutzung
```sh
Variable assignment (no spaces around =)
name="John Doe" age=30 path="/home/user"
Using variables
echo $name echo $\\{name\\} echo "Hello, $name" echo 'Literal: $name' # Single quotes prevent expansion
Special variables
echo $0 # Script name echo $1 # First argument echo $2 # Second argument echo $# # Number of arguments echo $@ # All arguments as separate words echo $* # All arguments as single word echo $ # Process ID echo $? # Exit status of last command echo $! # PID of last background job ```_
Parametererweiterung
```sh
Basic parameter expansion
echo $\\{variable\\} echo $\\{variable:-default\\} # Use default if unset or null echo $\\{variable:=default\\} # Set default if unset or null echo $\\{variable:+alternate\\} # Use alternate if set and not null echo $\\{variable:?error\\} # Error if unset or null
String length
string="Hello, World!" echo $\\{#string\\} # 13
Substring removal (POSIX)
filename="document.txt" echo $\\{filename%.\\} # Remove shortest match from end: document echo $\\{filename%%.\\} # Remove longest match from end: document echo $\\{filename#.\\} # Remove shortest match from beginning: txt echo $\\{filename##.\\} # Remove longest match from beginning: txt
Pattern matching examples
path="/usr/local/bin/command" echo $\\{path%/\\} # /usr/local/bin (dirname equivalent) echo $\\{path##/\\} # command (basename equivalent) ```_
Umweltvariablen
```sh
Set environment variables
export PATH="/usr/local/bin:$PATH" export EDITOR="vi" export LANG="en_US.UTF-8"
Unset variables
unset variable_name unset PATH # Dangerous!
Check if variable is set
if [ -n "$\\{VARIABLE+set\\}" ]; then echo "VARIABLE is set" fi
Default values for environment
: $\\{HOME:=/tmp\\} # Set HOME to /tmp if not set : $\\{USER:=nobody\\} # Set USER to nobody if not set ```_
Befehlsausübung und Substitution
Kommando Substitution
```sh
POSIX command substitution (preferred)
current_date=$(date) file_count=$(ls|wc -l) user_home=$(eval echo ~$USER)
Backtick substitution (legacy, avoid in new scripts)
current_date=date
file_count=ls|wc -l
Nested command substitution
echo "Today is $(date +%A), $(date +%B) $(date +%d)"
Command substitution in conditionals
if [ "$(whoami)" = "root" ]; then echo "Running as root" fi ```_
Ausführung des Befehls
```sh
Simple commands
ls -l echo "Hello, World!" date +"%Y-%m-%d %H:%M:%S"
Command with arguments
grep "pattern" file.txt find /path -name "*.txt" -type f
Background execution
long_running_command & background_pid=$! echo "Started background job with PID: $background_pid"
Wait for background jobs
wait $background_pid wait # Wait for all background jobs ```_
Pipeline und Umleitung
```sh
Pipes
ls -l|grep "txt" | ps aux | grep "process" | awk '\\{print $2\\}' | | cat file.txt | sort | uniq |
Output redirection
command > file.txt # Redirect stdout command 2> error.log # Redirect stderr command > output.log 2>&1 # Redirect both stdout and stderr command >> file.txt # Append stdout
Input redirection
command < input.txt sort < unsorted.txt > sorted.txt
Here documents
cat << EOF This is a here document Variables like $HOME are expanded EOF
Here documents with quoted delimiter (no expansion)
cat << 'EOF' This is a literal here document Variables like $HOME are not expanded EOF ```_
Kontrollstrukturen
Bedingte Aussagen
```sh
if-then-else
if [ condition ]; then echo "Condition is true" elif [ other_condition ]; then echo "Other condition is true" else echo "No condition is true" fi
Test conditions
if [ "$var" = "value" ]; then # String equality echo "String match" fi
if [ "$var" != "value" ]; then # String inequality echo "String mismatch" fi
if [ -z "$var" ]; then # String is empty echo "Variable is empty" fi
if [ -n "$var" ]; then # String is not empty echo "Variable is not empty" fi
Numeric comparisons
if [ "$num" -eq 10 ]; then # Equal echo "Number is 10" fi
if [ "$num" -ne 10 ]; then # Not equal echo "Number is not 10" fi
if [ "$num" -gt 5 ]; then # Greater than echo "Number is greater than 5" fi
if [ "$num" -lt 20 ]; then # Less than echo "Number is less than 20" fi
if [ "$num" -ge 5 ]; then # Greater than or equal echo "Number is 5 or greater" fi
if [ "$num" -le 20 ]; then # Less than or equal echo "Number is 20 or less" fi ```_
File Test Operators
```sh
File existence and type tests
if [ -e "file" ]; then # File exists echo "File exists" fi
if [ -f "file" ]; then # Regular file echo "Is a regular file" fi
if [ -d "directory" ]; then # Directory echo "Is a directory" fi
if [ -L "link" ]; then # Symbolic link echo "Is a symbolic link" fi
if [ -p "pipe" ]; then # Named pipe echo "Is a named pipe" fi
if [ -S "socket" ]; then # Socket echo "Is a socket" fi
File permission tests
if [ -r "file" ]; then # Readable echo "File is readable" fi
if [ -w "file" ]; then # Writable echo "File is writable" fi
if [ -x "file" ]; then # Executable echo "File is executable" fi
if [ -s "file" ]; then # File has size > 0 echo "File is not empty" fi
File comparison
if [ "file1" -nt "file2" ]; then # file1 newer than file2 echo "file1 is newer" fi
if [ "file1" -ot "file2" ]; then # file1 older than file2 echo "file1 is older" fi
if [ "file1" -ef "file2" ]; then # Same file (hard links) echo "Files are the same" fi ```_
Logische Operatoren
```sh
AND operator
if [ condition1 ] && [ condition2 ]; then echo "Both conditions are true" fi
OR operator
| if [ condition1 ] | | [ condition2 ]; then | echo "At least one condition is true" fi
NOT operator
if ! [ condition ]; then echo "Condition is false" fi
Complex logical expressions
if [ "$age" -ge 18 ] && [ "$age" -le 65 ]; then echo "Working age" fi
| if [ "$status" = "active" ] | | [ "$force" = "true" ]; then | echo "Proceeding" fi ```_
Rechtssachen
```sh
Basic case statement
case $variable in pattern1) echo "Matched pattern1" ;; pattern2|pattern3) echo "Matched pattern2 or pattern3" ;; *) echo "No pattern matched" ;; esac
Case with file extensions
case $filename in .txt) echo "Text file" ;; | .jpg | .png | .gif) | echo "Image file" ;; .sh) echo "Shell script" ;; ) echo "Unknown file type" ;; esac
Case with command line options
case $1 in -h|--help) show_help ;; -v|--version) show_version ;; -) echo "Unknown option: $1" exit 1 ;; ) process_file "$1" ;; esac ```_
Loops und Iteration
Für Loops
```sh
For loop with list
for item in apple banana orange; do echo "Fruit: $item" done
For loop with file glob
for file in *.txt; do echo "Processing: $file" done
For loop with command substitution
for user in $(cat users.txt); do echo "User: $user" done
For loop with positional parameters
for arg in "$@"; do echo "Argument: $arg" done
For loop with range (if seq is available)
for i in $(seq 1 10); do echo "Number: $i" done
C-style for loop (not POSIX, but common)
Use while loop instead for POSIX compliance
i=1 while [ $i -le 10 ]; do echo "Counter: $i" i=$((i + 1)) done ```_
Während Loops
```sh
Basic while loop
counter=1 while [ $counter -le 10 ]; do echo "Counter: $counter" counter=$((counter + 1)) done
While loop reading from file
while read line; do echo "Line: $line" done < file.txt
While loop with IFS
while IFS=: read user pass uid gid gecos home shell; do echo "User: $user, Home: $home, Shell: $shell" done < /etc/passwd
Infinite loop
while true; do echo "Press Ctrl+C to stop" sleep 1 done
While loop with condition
while [ -f "lockfile" ]; do echo "Waiting for lock to be released..." sleep 5 done ```_
Bis zu Loops
```sh
Basic until loop
counter=1 until [ $counter -gt 10 ]; do echo "Counter: $counter" counter=$((counter + 1)) done
Until loop waiting for condition
until [ -f "ready.flag" ]; do echo "Waiting for ready flag..." sleep 2 done
Until loop with command
until ping -c 1 google.com >/dev/null 2>&1; do echo "Waiting for network connection..." sleep 5 done ```_
Loop Control
```sh
Break and continue
for i in $(seq 1 10); do if [ $i -eq 5 ]; then continue # Skip iteration fi if [ $i -eq 8 ]; then break # Exit loop fi echo $i done
Nested loops with labeled break (not POSIX standard)
Use functions or flags instead
found=false for dir in /usr /opt /var; do for file in "$dir"/*; do if [ "$file" = "/usr/bin/vim" ]; then echo "Found vim at $file" found=true break fi done if [ "$found" = "true" ]; then break fi done ```_
Funktionen
Funktion Definition und Nutzung
```sh
POSIX function definition
function_name() \\{ echo "This is a function" \\}
Alternative syntax (POSIX)
function_name() \\{ echo "This is a function" \\}
Function with parameters
greet() \\{ echo "Hello, $1!" \\}
Function with multiple parameters
calculate_sum() \\{ result=$(($1 + $2)) echo $result \\}
Function with local variables (not POSIX, use carefully)
process_file() \\{ filename="$1" line_count=$(wc -l < "$filename") echo "File $filename has $line_count lines" \\}
Function usage
greet "World" sum=$(calculate_sum 5 3) echo "Sum: $sum" ```_
Funktion Best Practices
```sh
Function with error handling
safe_copy() \\{
if [ $# -ne 2 ]; then
echo "Usage: safe_copy
source_file="$1"
dest_file="$2"
if [ ! -f "$source_file" ]; then
echo "Error: Source file '$source_file' does not exist" >&2
return 1
fi
if [ -f "$dest_file" ]; then
echo "Warning: Destination file '$dest_file' already exists" >&2
printf "Overwrite? (y/N): "
read answer
case $answer in
[Yy]|[Yy][Ee][Ss])
;;
*)
echo "Copy cancelled"
return 1
;;
esac
fi
if cp "$source_file" "$dest_file"; then
echo "Successfully copied '$source_file' to '$dest_file'"
return 0
else
echo "Error: Failed to copy file" >&2
return 1
fi
\\}
Function with return values
is_number() \\{ case $1 in ''|[!0-9]) return 1 ;; # Not a number *) return 0 ;; # Is a number esac \\}
Usage
if is_number "$input"; then echo "$input is a number" else echo "$input is not a number" fi ```_
Funktionsbibliotheken
```sh
Create function library file: lib.sh
!/bin/sh
Logging functions
log_info() \\{ echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S'): $*" \\}
log_error() \\{ echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S'): $*" >&2 \\}
log_debug() \\{ if [ "$DEBUG" = "1" ]; then echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S'): $*" >&2 fi \\}
File utilities
backup_file() \\{ if [ -f "$1" ]; then cp "$1" "$1.backup.$(date +%Y%m%d_%H%M%S)" log_info "Backed up $1" fi \\}
Source library in main script
. ./lib.sh
Use library functions
log_info "Script started" backup_file "important.conf" log_debug "Debug information" ```_
Arithmetische Operationen
POSIX Arithmetische Erweiterung
```sh
Basic arithmetic
result=$((5 + 3)) # 8 result=$((10 - 4)) # 6 result=$((6 * 7)) # 42 result=$((20 / 4)) # 5 result=$((17 % 5)) # 2 (modulo)
Arithmetic with variables
num1=10 num2=5 sum=$((num1 + num2)) # 15 product=$((num1 * num2)) # 50
Increment and decrement
counter=0 counter=$((counter + 1)) # Increment counter=$((counter - 1)) # Decrement
Comparison in arithmetic context
if [ $((num1 > num2)) -eq 1 ]; then echo "num1 is greater than num2" fi
Complex expressions
result=$(( (num1 + num2) * 2 )) result=$(( num1 ** 2 )) # Exponentiation (if supported) ```_
Externe Arithmetik Werkzeuge
```sh
Using expr (portable but slower)
result=$(expr 5 + 3) result=$(expr $num1 * $num2) # Note: * must be escaped result=$(expr $num1 / $num2)
Using bc for floating point
result=$(echo "scale=2; 10/3"|bc) result=$(echo "scale=4; sqrt(16)"|bc -l)
Using awk for calculations
result=$(awk "BEGIN \\{print 10/3\\}") result=$(awk "BEGIN \\{printf \"%.2f\", 10/3\\}") ```_
Eingabe und Ausgabe
Reading Input
```sh
Read single line
echo "Enter your name: " read name echo "Hello, $name"
Read with prompt
read -p "Enter your age: " age
Read multiple variables
echo "Enter first and last name: " read first last echo "Hello, $first $last"
Read with timeout (if supported)
if read -t 10 -p "Enter input (10 seconds): " input; then echo "You entered: $input" else echo "Timeout reached" fi
Read password (hidden input, if supported)
read -s -p "Enter password: " password echo # New line after hidden input
Read from file
while read line; do echo "Line: $line" done < file.txt ```_
Ausgabeformatierung
```sh
Basic output
echo "Simple message" printf "Formatted message\n"
Printf formatting
printf "Name: %s, Age: %d\n" "$name" "$age" printf "Price: $%.2f\n" "$price" printf "Hex: %x, Octal: %o\n" "$number" "$number"
Escape sequences
echo "Line 1\nLine 2" # May not work in all shells printf "Line 1\nLine 2\n" # Portable
Output to stderr
echo "Error message" >&2 printf "Error: %s\n" "$error_msg" >&2 ```_
Dateioperationen
```sh
Create files
touch file.txt
file.txt # Create empty file
Write to files
echo "Content" > file.txt # Overwrite echo "More content" >> file.txt # Append
Read files
content=$(cat file.txt) while read line; do echo "Line: $line" done < file.txt
Copy files
cp source.txt destination.txt
Move/rename files
mv old_name.txt new_name.txt
Remove files
rm file.txt rm -f file.txt # Force removal rm -r directory/ # Recursive removal ```_
Fehlerbehandlung und Debugging
Exit Status und Fehlersuche
```sh
Check command success
if command; then echo "Command succeeded" else echo "Command failed with exit code $?" fi
Alternative syntax
| command && echo "Success" | | echo "Failed" |
Set exit status
exit 0 # Success exit 1 # General error exit 2 # Misuse of shell builtins
Function return values
my_function() \\{ if [ condition ]; then return 0 # Success else return 1 # Failure fi \\}
if my_function; then echo "Function succeeded" fi ```_
Fehler Handling Muster
```sh
Exit on error
set -e command_that_might_fail echo "This won't execute if command fails"
Disable exit on error for specific command
set -e if ! command_that_might_fail; then echo "Command failed, but script continues" fi
Error handling with cleanup
cleanup() \\{ echo "Cleaning up..." rm -f temp_file \\}
trap cleanup EXIT # Run cleanup on script exit trap cleanup INT TERM # Run cleanup on interrupt/terminate
Validate input
validate_file() \\{ if [ ! -f "$1" ]; then echo "Error: File '$1' does not exist" >&2 exit 1 fi \\}
validate_file "$input_file" ```_
Debugging Techniques
```sh
Debug mode
set -x # Print commands as executed set +x # Disable debug mode
Verbose mode
set -v # Print input lines as read set +v # Disable verbose mode
Check syntax without execution
sh -n script.sh
Debug function
debug() \\{ if [ "$DEBUG" = "1" ]; then echo "DEBUG: $*" >&2 fi \\}
Usage
DEBUG=1 ./script.sh
Trace function calls
trace() \\{ echo "TRACE: Entering function $1" >&2 \\}
my_function() \\{ trace "my_function" # Function body \\} ```_
Schriftstruktur und Best Practices
Script Vorlage
```sh
!/bin/sh
Script description
Author: Your Name
Date: YYYY-MM-DD
Version: 1.0
Exit on error
set -e
Global variables
SCRIPT_NAME=$(basename "$0") SCRIPT_DIR=$(dirname "$0") VERSION="1.0"
Functions
usage() \\{ cat << EOF Usage: $SCRIPT_NAME [OPTIONS] [ARGUMENTS]
Description of what the script does.
OPTIONS: -h, --help Show this help message -v, --version Show version information -d, --debug Enable debug mode
ARGUMENTS: file Input file to process
EXAMPLES: $SCRIPT_NAME input.txt $SCRIPT_NAME -d input.txt
EOF \\}
version() \\{ echo "$SCRIPT_NAME version $VERSION" \\}
main() \\{ # Parse command line arguments while [ $# -gt 0 ]; do case $1 in -h|--help) usage exit 0 ;; -v|--version) version exit 0 ;; -d|--debug) DEBUG=1 set -x shift ;; -) echo "Error: Unknown option $1" >&2 usage >&2 exit 1 ;; ) break ;; esac done
# Validate arguments
if [ $# -eq 0 ]; then
echo "Error: No input file specified" >&2
usage >&2
exit 1
fi
input_file="$1"
# Validate input file
if [ ! -f "$input_file" ]; then
echo "Error: File '$input_file' does not exist" >&2
exit 1
fi
# Main script logic
echo "Processing file: $input_file"
# Add your code here
echo "Script completed successfully"
\\}
Run main function with all arguments
main "$@" ```_
Sicherheitsbedenken
```sh
Use POSIX-compliant constructs
[ condition ] instead of [[ condition ]]
$(command) instead of command
$((arithmetic)) instead of $[arithmetic]
Avoid bash-specific features
No: array=(element1 element2)
Yes: Use space-separated strings or multiple variables
No: [[ string =~ regex ]]
Yes: Use case or expr for pattern matching
No: $\\{parameter,,\\} (lowercase)
Yes: Use tr or awk for case conversion
Portable shebang
!/bin/sh # Most portable
!/usr/bin/env sh # Alternative
Check for required commands
| command -v required_command >/dev/null 2>&1 | | \\{ | echo "Error: required_command is not installed" >&2 exit 1 \\}
Portable temporary files
temp_file="$\\{TMPDIR:-/tmp\\}/script.$" trap 'rm -f "$temp_file"' EXIT ```_
Sicherheit Best Practices
```sh
Quote variables to prevent word splitting
rm "$filename" # Correct rm $filename # Dangerous
Validate input
case $input in [a-zA-Z0-9_-]) # Valid input ;; ) echo "Invalid input" >&2 exit 1 ;; esac
Use full paths for commands in scripts
/bin/rm "$file" /usr/bin/find "$dir" -name "*.tmp"
Set secure umask
umask 077 # Restrictive permissions
Avoid eval with user input
eval "$user_input" # Dangerous
Use case statements or functions instead
Handle signals properly
cleanup() \\{ rm -f "$temp_file" exit 1 \\} trap cleanup INT TERM ```_
Die POSIX Shell dient als universelle Grundlage für Shell-Skripting über Unix-ähnliche Systeme, die wesentliche Funktionalität bietet und gleichzeitig maximale Portabilität behält. Seine standardisierte Syntax und Features sorgen dafür, dass die für POSIX sh geschriebenen Skripte über unterschiedliche Plattformen hinweg, von minimalen eingebetteten Systemen bis hin zu großen Unternehmensservern, funktionieren. Obwohl es an den fortschrittlichen Funktionen moderner Shells fehlt, ist das Verständnis von POSIX sh grundlegend für das Schreiben robuster, tragbarer Shell-Skripte, die den Test der Zeit und arbeiten zuverlässig in verschiedenen Rechenumgebungen.