Przejdź do treści

📜 POSIX Shell Reference

POSIX (Portable Operating System Interface) defines the standard for Unix-like operating systems, including the shell and utilities. Understanding POSIX shell requirements is crucial for writing portable scripts that work across different systems.


🎯 POSIX Shell Requirements

What is POSIX Shell?

POSIX shell is a standardized specification that defines: - Shell command language syntax and semantics - Built-in utilities and their behavior - Environment variable conventions - File system interface standards - Process control mechanisms

POSIX Compliant Shells

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Standard POSIX shells:
# - sh (Bourne shell)
# - dash (Debian Almquist Shell)
# - ash (Almquist Shell)
# - ksh (KornShell) in POSIX mode
# - bash in POSIX mode (bash --posix)
# - zsh in POSIX mode (zsh --emulate sh)

# Check if shell is POSIX compliant
if [ -n "$POSIXLY_CORRECT" ] || [ "$(basename "$0")" = "sh" ]; then
    echo "Running in POSIX mode"
fi

🔧 POSIX Shell Syntax

Variable Assignment

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# ✅ POSIX compliant variable assignment
variable=value
export VARIABLE=value
readonly CONSTANT=value

# ❌ Not POSIX compliant
array=(item1 item2)        # Arrays not supported
local var=value            # 'local' not in POSIX
variable+=value            # += operator not in POSIX

# ✅ POSIX alternatives
# For arrays, use delimited strings:
ITEMS="item1:item2:item3"
IFS=':' read -ra item_array <<< "$ITEMS"

# For local variables:
var=value  # Just use regular variables in functions

# For string appending:
variable="${variable}value"

Conditional Expressions

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# ✅ POSIX compliant tests
if [ "$var" = "value" ]; then
    echo "Equal"
fi

if [ "$num" -eq 5 ]; then
    echo "Equal to 5"
fi

if [ -f "$file" ]; then
    echo "File exists"
fi

# ❌ Not POSIX compliant
if [[ "$var" == pattern ]]; then     # [[ ]] not in POSIX
if [ "$var" =~ regex ]; then         # =~ not in POSIX
if (( num > 5 )); then               # (( )) not in POSIX

# ✅ POSIX alternatives
# Pattern matching:
case "$var" in
    pattern*)
        echo "Matches pattern"
        ;;
esac

# Arithmetic comparison:
if [ "$((num))" -gt 5 ]; then
    echo "Greater than 5"
fi

Command Substitution

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# ✅ POSIX compliant
result=$(command)
result=`command`  # Backticks also work but less readable

# ✅ Nested command substitution
outer=$(command1 $(command2))

# ❌ Not POSIX compliant
result=$(command1; command2)  # Command lists in $() may not be portable

# ✅ POSIX alternative
result=$(command1 && command2)

📋 POSIX Built-in Commands

Essential Built-ins

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# ✅ Required POSIX built-ins:
# break, cd, continue, eval, exec, exit, export
# getopts, hash, pwd, readonly, return, shift
# test, times, trap, unset

# ✅ Standard utilities (must be available):
# basename, cat, chmod, cmp, cp, date, dd, df
# echo, expr, false, find, grep, kill, ln, ls
# mkdir, mv, ps, pwd, rm, rmdir, sed, sh
# sleep, sort, stty, sync, tar, touch, true
# uname, wc, who

# Example usage:
cd /path/to/directory
export PATH="/usr/local/bin:$PATH"
readonly CONFIG_FILE="/etc/myapp.conf"
shift 2  # Shift positional parameters

Test Command Variants

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# ✅ POSIX compliant test syntax
[ "$var" = "value" ]           # String equality
[ "$num" -eq 5 ]               # Numeric equality
[ -f "$file" ]                 # File exists and is regular file
[ -d "$dir" ]                  # Directory exists
[ -r "$file" ]                 # File is readable
[ -w "$file" ]                 # File is writable
[ -x "$file" ]                 # File is executable
[ "$str1" != "$str2" ]         # String inequality
[ "$num1" -lt "$num2" ]        # Less than
[ "$num1" -gt "$num2" ]        # Greater than
[ "$num1" -le "$num2" ]        # Less than or equal
[ "$num1" -ge "$num2" ]        # Greater than or equal
[ -z "$str" ]                  # String is zero length
[ -n "$str" ]                  # String is non-zero length

# ❌ Not POSIX compliant
[ "$var" == "value" ]          # == not in POSIX (use =)
[ "$var" = value ]             # Right side should be quoted
[ $num -eq 05 ]                # Leading zeros may cause octal interpretation

🔄 Control Structures

Loops

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# ✅ POSIX compliant loops

# For loop with word list
for var in word1 word2 word3; do
    echo "$var"
done

# For loop with positional parameters
for arg in "$@"; do
    echo "$arg"
done

# While loop
while [ "$condition" = "true" ]; do
    # loop body
    condition=$(get_new_condition)
done

# Until loop
until [ "$condition" = "false" ]; do
    # loop body
    condition=$(get_new_condition)
done

# ❌ Not POSIX compliant
for (( i=0; i<10; i++ )); do   # C-style for loop not in POSIX
    echo "$i"
done

# ✅ POSIX alternative for counting
i=0
while [ $i -lt 10 ]; do
    echo "$i"
    i=$((i + 1))
done

Case Statement

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# ✅ POSIX compliant case statement
case "$variable" in
    pattern1)
        echo "Matches pattern1"
        ;;
    pattern2|pattern3)
        echo "Matches pattern2 or pattern3"
        ;;
    *)
        echo "No match"
        ;;
esac

# ❌ Extended patterns not in POSIX
case "$var" in
    !(pattern))    # Not in POSIX
        ;;
    +(pattern))    # Not in POSIX
        ;;
esac

📊 I/O Redirection

Standard Redirection Operators

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# ✅ POSIX compliant redirection

# Basic redirection
command > file.txt           # Standard output to file
command < file.txt           # Standard input from file
command 2> error.log         # Standard error to file
command > output.log 2>&1    # Both stdout and stderr to file
command >> file.txt          # Append to file
command 2>> error.log        # Append error to file

# Here documents
cat << EOF
This is a here document
Variable expansion: $HOME
EOF

# Here documents with quoted delimiter (no expansion)
cat << 'EOF'
No variable expansion here: $HOME
EOF

# Here strings (limited support)
# Note: Here strings (<<<) are not in POSIX
# Use here documents instead:
command << EOF
single line input
EOF

# ❌ Not in POSIX
command <<< "input"          # Here strings not POSIX
command &> file.txt          # Combined redirect not POSIX

🛠️ Functions and Parameters

Function Definition

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# ✅ POSIX compliant function definition
my_function() {
    echo "Hello from function"
    echo "Arguments: $@"
    echo "First argument: $1"
    echo "Number of arguments: $#"
    return 0
}

# Call function
my_function arg1 arg2

# ❌ Not in POSIX
function my_function {        # 'function' keyword not in POSIX
    # function body
}

# ✅ Both forms work, but () is more portable

Parameter Expansion

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# ✅ POSIX compliant parameter expansion
var="hello world"

echo "$var"          # hello world
echo "${var}"        # hello world
echo "${var:-default}"  # Use default if var is unset or empty
echo "${var:=default}"  # Set and use default if var is unset or empty
echo "${var:+value}"    # Use value if var is set and non-empty
echo "${var#pattern}"   # Remove shortest match from beginning
echo "${var##pattern}"  # Remove longest match from beginning
echo "${var%pattern}"   # Remove shortest match from end
echo "${var%%pattern}"  # Remove longest match from end

# ❌ Not in POSIX
echo "${var^^}"      # Case conversion not in POSIX
echo "${var,,}"      # Case conversion not in POSIX
echo "${var:0:5}"    # Substring not in POSIX
echo "${#var}"       # Length not in POSIX (but widely supported)

# ✅ POSIX alternatives for unsupported features
# Length:
expr length "$var"   # Or: echo "$var" | wc -c

# Substring:
echo "$var" | cut -c1-5

# Case conversion:
echo "$var" | tr '[:lower:]' '[:upper:]'

🎨 Advanced POSIX Features

Signal Handling

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# ✅ POSIX compliant signal handling
trap 'echo "Caught SIGINT"' INT
trap 'echo "Caught SIGTERM"' TERM
trap 'echo "Script exiting"' EXIT
trap 'echo "Caught error"' ERR  # Note: ERR trap not in POSIX

# List available signals
kill -l

# Send signals
kill -TERM 1234
kill -INT $$

# ❌ Not in POSIX
trap 'echo "Caught error"' ERR  # ERR trap is bash-specific

Job Control

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# ✅ POSIX job control features
command &           # Run in background
jobs                # List jobs (may not be available)
fg %1               # Foreground job 1
bg %1               # Background job 1
kill %1             # Kill job 1

# Wait for background jobs
command1 &
command2 &
wait                # Wait for all background jobs
wait $!             # Wait for last background job

🔍 POSIX Compliance Testing

Testing Scripts for POSIX Compliance

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# ✅ Test with POSIX shell
dash script.sh

# ✅ Use shellcheck with POSIX mode
shellcheck -s sh script.sh

# ✅ Check syntax with different shells
for shell in sh dash bash ksh; do
    if command -v "$shell" >/dev/null 2>&1; then
        echo "Testing with $shell..."
        "$shell" -n script.sh || echo "Syntax error in $shell"
    fi
done

# ✅ POSIX compliance checker
check_posix_compliance() {
    local script="$1"

    # Check for common non-POSIX constructs
    if grep -q '\[\[' "$script"; then
        echo "Warning: [[ ]] found (not POSIX)"
    fi

    if grep -q 'function ' "$script"; then
        echo "Warning: 'function' keyword found (prefer POSIX syntax)"
    fi

    if grep -q '\(\(' "$script"; then
        echo "Warning: (( )) found (not POSIX)"
    fi

    if grep -q '<<<' "$script"; then
        echo "Warning: Here strings found (not POSIX)"
    fi

    # Test with dash
    if command -v dash >/dev/null 2>&1; then
        if ! dash -n "$script"; then
            echo "Error: dash reports syntax errors"
        fi
    fi
}

POSIX Environment Variables

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# ✅ Standard POSIX environment variables
echo "$HOME"      # Home directory
echo "$PATH"      # Command search path
echo "$PWD"       # Current working directory
echo "$OLDPWD"    # Previous working directory
echo "$SHELL"     # Login shell
echo "$TERM"      # Terminal type
echo "$USER"      # Username
echo "$LOGNAME"   # Login name

# ✅ POSIX special parameters
echo "$*"         # All positional parameters as single string
echo "$@"         # All positional parameters as separate strings
echo "$#"         # Number of positional parameters
echo "$?"         # Exit status of last command
echo "$$"         # Process ID of current shell
echo "$!"         # Process ID of last background command
echo "$0"         # Name of current script

🧾 Summary Guidelines

POSIX Compliance Checklist

✅ Use #!/bin/sh shebang for maximum portability ✅ Quote all variable expansions: "$var" ✅ Use [ ] instead of [[ ]] for tests ✅ Avoid arrays (use delimited strings instead) ✅ Use standard parameter expansion only ✅ Avoid here strings (<<<) ✅ Use standard redirection operators only ✅ Test with multiple POSIX shells (dash, ash) ✅ Avoid shell-specific built-ins and keywords

When to Use POSIX Shell

Use POSIX when: - Writing portable scripts for multiple systems - Targeting minimal environments (containers, embedded) - Need maximum compatibility - Following organizational standards - Writing system initialization scripts

Consider alternatives when: - Need advanced features (arrays, associative arrays) - Want better error handling and debugging - Require interactive shell enhancements - Need extensive pattern matching capabilities


🧠 Portable Script Template

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#!/bin/sh
# POSIX compliant script template

# Set strict mode (careful with this in some shells)
# set -e  # Exit on error
# set -u  # Exit on undefined variables

# Error handling function
error_exit() {
    echo "Error: $1" >&2
    exit "${2:-1}"
}

# Input validation
validate_required() {
    # Check if variable is set
    if [ -z "$(eval echo \$$1)" ]; then
        error_exit "Required parameter $1 is not set"
    fi
}

# Safe command execution
safe_execute() {
    "$@" || error_exit "Command failed: $*"
}

# Main function
main() {
    # Validate inputs
    # validate_required "INPUT_FILE"

    # Your logic here
    echo "Running POSIX compliant script"
}

# Run main function
main "$@"

🧾 See Also