bash-53-features

Installation
SKILL.md

Bash 5.3 Features and Improvements

Released in July 2025, Bash 5.3 introduces significant enhancements including revolutionary command substitution syntax, new variables, loadable builtins, and improved C standard conformance.

Revolutionary Command Substitution

Efficient In-Shell Execution: ${ command; }

Execute commands without forking, dramatically improving performance:

# Traditional command substitution (creates subshell)
result=$(echo "Hello, World")

# NEW: In-shell command substitution (no fork!)
# Note: a space (or tab/newline/|) is required after the opening '{'
result=${ echo "Hello, World"; }

# Practical example: Fast variable assignment
config_value=${ grep "^timeout=" config.txt | cut -d= -f2; }

# Performance comparison function
benchmark_substitution() {
    local i
    local start end

    echo "Testing traditional substitution..."
    start="${EPOCHREALTIME}"
    for ((i = 0; i < 1000; i++)); do
        result=$(echo "${i}")
    done
    end="${EPOCHREALTIME}"
    printf 'Traditional: %.4f seconds\n' \
        "$(awk "BEGIN {print ${end} - ${start}}")"

    echo "Testing new in-shell substitution..."
    start="${EPOCHREALTIME}"
    for ((i = 0; i < 1000; i++)); do
        result=${ echo "${i}"; }
    done
    end="${EPOCHREALTIME}"
    printf 'In-shell: %.4f seconds\n' \
        "$(awk "BEGIN {print ${end} - ${start}}")"
}

benchmark_substitution

Benefits:

  • No subprocess creation overhead
  • Significantly faster for simple commands
  • Reduced resource usage in loops
  • Same syntax familiarity as command substitution

REPLY Variable Capture: ${| command; }

Execute commands and automatically store output in REPLY. Note: REPLY is local to the substitution — its value is restored after completion, so capture it immediately:

Code examples

Benefits:

  • Cleaner code without intermediate variables
  • Consistent capture mechanism
  • Reduced visual clutter
  • Perfect for rapid prototyping

Use Cases Comparison

# String manipulation - use in-shell for efficiency
filename="document.txt"
basename=${ echo "${filename%.*}"; }
extension=${ echo "${filename##*.}"; }

# Output capture - use REPLY for clarity
${| df -h / | tail -1 | awk '{print $5}'; }
disk_usage="${REPLY}"
echo "Disk usage: ${disk_usage}"

# Complex pipelines - traditional might still be clearer
result=$(cat file.txt | grep pattern | sort | uniq)

GLOBSORT Variable

Control the sorting order of filename and pathname expansion. The specifier is optionally prefixed with + (ascending, default) or - (descending):

Code examples

Available sort specifiers:

  • name — Alphabetical by filename
  • size — By file size
  • mtime — By modification time
  • atime — By access time
  • ctime — By inode change time
  • blocks — By allocated block count
  • numeric — Numeric sort on leading digits in filename
  • nosort — Disable sorting (glob order)
  • Prefix: + (ascending, default) or - (descending)

Enhanced Builtins

compgen with Variable Storage

Store completions directly in a variable:

Code examples

read with Readline Completion (-E)

Interactive input with autocompletion:

# Enable readline completion during read
choose_file() {
    local file

    echo "Enter filename (tab for completion):"
    read -e -r -E -p "> " file

    if [[ -f "${file}" ]]; then
        echo "Selected: ${file}"
        return 0
    else
        echo "File not found: ${file}"
        return 1
    fi
}

choose_file

# Practical example: Interactive configuration
configure_app() {
    local config_file

    echo "Select configuration file:"
    read -e -r -E -p "Config: " config_file

    if [[ -f "${config_file}" ]]; then
        ${| grep -c "^[^#]" "${config_file}"; }
        local line_count="${REPLY}"
        echo "Found ${line_count} active configuration lines"
    fi
}

source with Path (-p)

Specify search path for sourced scripts:

Code examples

printf Enhancements

New options for multibyte strings and representations:

# Enhanced printf options
printf '%q\n' "string with spaces"  # Shell-quoted output

# NEW: Multibyte string support improvements
text="Hello, 世界"
printf 'Length: %d bytes\n' "${#text}"

# Practical example: Safe command construction
build_command() {
    local -a args=("$@")
    local arg cmd=""

    for arg in "${args[@]}"; do
        cmd+=$(printf '%q ' "${arg}")
    done

    echo "Safe command: ${cmd}"
}

build_command ls "-l" "file with spaces.txt"

New Loadable Builtins

kv - Key-Value Arrays

Create associative arrays from key-value data. Note: The kv builtin existence is confirmed in Bash 5.3; the exact interface shown below is illustrative — verify with help kv after loading:

Code examples

strptime - Date Parsing

Parse textual dates into Unix timestamps. Note: The strptime builtin existence is confirmed in Bash 5.3; the exact interface shown below is illustrative — verify with help strptime after loading:

Code examples

fltexpr - Floating-Point Calculations

Perform floating-point arithmetic without external tools:

Code examples

POSIX Mode Improvements

Enhanced POSIX compliance:

# Enable POSIX mode
set -o posix

# String comparisons now follow locale rules
[[ "ä" < "z" ]]  # Locale-dependent comparison

# Improved POSIX conformance in builtins
# test, trap, wait, bind all more strictly conformant

# Practical example: Portable script header
#!/usr/bin/env bash

if [[ "${BASH_VERSINFO[0]}" -ge 5 ]] && [[ "${BASH_VERSINFO[1]}" -ge 3 ]]; then
    # Bash 5.3+ available, use modern features
    USE_MODERN_FEATURES=1
else
    # Fall back to POSIX mode for portability
    set -o posix
    USE_MODERN_FEATURES=0
fi

Improved Error Reporting

More detailed error messages:

Code examples

C Standard Conformance Improvements

Bash 5.3 improves conformance to modern C standards (the build minimum remains C90):

  • No longer compiles with K&R C compilers
  • Modernized codebase for better conformance
  • Better optimization opportunities
  • Improved type safety

Note: This primarily affects developers compiling Bash from source, not end users.

Performance Improvements

  • Command substitution without forking (${ cmd; }) dramatically reduces overhead
  • Optimized globbing with GLOBSORT
  • Faster builtin operations
  • Reduced memory usage in various operations
# Benchmark: Traditional vs new substitution
benchmark() {
    local iterations=10000
    local i start end

    start="${EPOCHREALTIME}"
    for ((i = 0; i < iterations; i++)); do
        result=$(echo test)
    done
    end="${EPOCHREALTIME}"
    printf 'Traditional: %.4f seconds\n' \
        "$(awk "BEGIN {print ${end} - ${start}}")"

    start="${EPOCHREALTIME}"
    for ((i = 0; i < iterations; i++)); do
        result=${ echo test; }
    done
    end="${EPOCHREALTIME}"
    printf 'In-shell: %.4f seconds\n' \
        "$(awk "BEGIN {print ${end} - ${start}}")"
}

benchmark

Migration Guide

From Bash 5.2 to 5.3

Most scripts compatible, but consider:

# Take advantage of new command substitution for performance
# OLD:
for file in *; do
    size=$(stat -f%z "${file}" 2>/dev/null)
done

# NEW (faster):
for file in *; do
    ${| stat -f%z "${file}" 2>/dev/null; }
    size="${REPLY}"
done

# Use GLOBSORT for better file processing
GLOBSORT="-mtime"
for file in *.log; do
    process_recent_log "${file}"
done

# Leverage new builtins where appropriate
# Instead of: awk calculation
# Use: fltexpr for floating-point math

Version Detection

# Check for Bash 5.3 features
if [[ "${BASH_VERSINFO[0]}" -ge 5 ]] && [[ "${BASH_VERSINFO[1]}" -ge 3 ]]; then
    echo "Bash 5.3+ features available"
    CAN_USE_INSITU_SUBSTITUTION=1
    CAN_USE_GLOBSORT=1
else
    echo "Bash version: ${BASH_VERSION}"
    CAN_USE_INSITU_SUBSTITUTION=0
    CAN_USE_GLOBSORT=0
fi

References

Additional Resources

For broader Bash development patterns and best practices, see:

Related skills

More from jamie-bitflight/claude_skills

Installs
5
GitHub Stars
40
First Seen
Mar 29, 2026