Zum Inhalt

sh - POSIX Shell

generieren

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 " >&2 return 1 fi

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.