| name | bash-53-features |
| description | Bash 5.3 release features and improvements with practical examples. Use when working with Bash 5.3 features, new command substitution, GLOBSORT, loadable builtins, or when user asks about Bash 5.3 changes, new features, or version-specific capabilities. |
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 C23 compliance.
Revolutionary Command Substitution
Efficient In-Shell Execution: ${command;}
Execute commands without forking, dramatically improving performance:
result=$(echo "Hello, World")
result=${echo "Hello, World";}
config_value=${grep "^timeout=" config.txt | cut -d= -f2;}
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:
${|date +%Y-%m-%d;}
echo "Today is: ${REPLY}"
get_system_info() {
${|uname -s;}
local os="${REPLY}"
${|uname -r;}
local kernel="${REPLY}"
${|hostname;}
local host="${REPLY}"
printf 'System: %s %s on %s\n' "${os}" "${kernel}" "${host}"
}
get_system_info
process_data() {
${|grep "ERROR" logfile.txt | wc -l;}
local error_count="${REPLY}"
${|grep "WARNING" logfile.txt | wc -l;}
local warning_count="${REPLY}"
printf 'Errors: %d, Warnings: %d\n' "${error_count}" "${warning_count}"
}
check_service() {
local service="${1}"
[[ == ]];
0
1
}
check_service
Benefits:
- Cleaner code without intermediate variables
- Consistent capture mechanism
- Reduced visual clutter
- Perfect for rapid prototyping
Use Cases Comparison
filename="document.txt"
basename=${echo "${filename%.*}";}
extension=${echo "${filename##*.}";}
${|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 completion:
GLOBSORT="name"
GLOBSORT="size"
GLOBSORT="date"
GLOBSORT="name:asc"
GLOBSORT="size:desc"
process_by_size() {
local dir="${1}"
GLOBSORT="size:desc"
for file in "${dir}"/*; do
[[ -f "${file}" ]] || continue
size=$(stat -f%z "${file}" 2>/dev/null || stat -c%s "${file}" 2>/dev/null)
printf 'Processing %s (%d bytes)\n' "${file}" "${size}"
done
}
process_by_size "/var/log"
process_newest() {
local dir="${1}"
local -a files
GLOBSORT="date:desc"
files=(/*)
file ;
[[ -f ]] ||
}
process_newest
Available sort modes:
name - Alphabetical by filename
size - By file size
date - By modification time
- Suffixes:
:asc (ascending), :desc (descending)
Enhanced Builtins
compgen with Variable Storage
Store completions directly in a variable:
compgen -V completions -c ba
echo "Commands starting with 'ba':"
for cmd in "${completions[@]}"; do
echo " ${cmd}"
done
get_available_commands() {
local prefix="${1}"
local -a commands
compgen -V commands -c "${prefix}"
if [[ ${#commands[@]} -eq 0 ]]; then
echo "No commands found starting with '${prefix}'"
return 1
fi
printf 'Available commands (%d):\n' "${#commands[@]}"
printf ' - %s\n' "${commands[@]}"
}
get_available_commands "git"
read with Readline Completion (-E)
Interactive input with autocompletion:
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
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}";}
echo "Found ${REPLY} active configuration lines"
fi
}
source with Path (-p)
Specify search path for sourced scripts:
source /opt/app/lib/utils.sh
source -p "/opt/app/lib:/usr/local/lib" utils.sh
load_library() {
local lib_name="${1}"
local -a search_paths=(
"${HOME}/.local/lib"
"/usr/local/lib"
"/opt/lib"
)
local search_path
search_path=$(IFS=:; echo "${search_paths[*]}")
if source -p "${search_path}" "${lib_name}" 2>/dev/null; then
echo "Loaded library: ${lib_name}"
return 0
else
echo "Failed to load library: ${lib_name}" >&2
return 1
fi
}
load_library "common.sh"
printf Enhancements
New options for multibyte strings and representations:
printf '%q\n' "string with spaces"
text="Hello, 世界"
printf 'Length: %d bytes\n' "${#text}"
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:
enable -f /usr/local/lib/bash/kv kv 2>/dev/null || true
declare -A config
kv config <<EOF
database_host=localhost
database_port=5432
database_name=myapp
api_key=secret123
debug_mode=true
EOF
echo "Database: ${config[database_host]}:${config[database_port]}"
echo "Debug: ${config[debug_mode]}"
load_env_config() {
local config_file="${1}"
declare -gA APP_CONFIG
while IFS='=' read -r key value; do
[[ "${key}" =~ ^[[:space:]]*# ]] && continue
[[ -z "${key}" ]] && continue
key="${key#"${key%%[![:space:]]*}"}"
value="${value#"${value%%[![:space:]]*}"}"
APP_CONFIG["${key}"]="${value}"
done < "${config_file}"
}
strptime - Date Parsing
Parse textual dates into Unix timestamps:
enable -f /usr/local/lib/bash/strptime strptime 2>/dev/null || true
date_string="2025-07-15 14:30:00"
timestamp=$(strptime "%Y-%m-%d %H:%M:%S" "${date_string}")
echo "Timestamp: ${timestamp}"
echo "Date: $(date -d "@${timestamp}" '+%Y-%m-%d %H:%M:%S')"
parse_log_timestamp() {
local log_line="${1}"
local timestamp_str date_format timestamp
timestamp_str="${log_line%% *}"
if [[ "${timestamp_str}" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then
date_format="%Y-%m-%d"
elif [[ "${timestamp_str}" =~ ^[0-9]{2}/[0-9]{2}/[0-9]{4}$ ]]; then
date_format="%m/%d/%Y"
else
echo "Unknown date format" >&2
return 1
fi
timestamp=$(strptime "${date_format}" "${timestamp_str}")
echo ""
}
parse_log_timestamp
fltexpr - Floating-Point Calculations
Perform floating-point arithmetic without external tools:
enable -f /usr/local/lib/bash/fltexpr fltexpr 2>/dev/null || true
result=$(fltexpr "3.14159 * 2.0")
echo "Result: ${result}"
calculate_average() {
local -a values=("$@")
local sum=0.0
local count=${#values[@]}
for value in "${values[@]}"; do
sum=$(fltexpr "${sum} + ${value}")
done
local average
average=$(fltexpr "${sum} / ${count}")
echo "${average}"
}
response_times=(0.123 0.456 0.234 0.567 0.321)
avg=$(calculate_average "${response_times[@]}")
echo "Average response time: ${avg}s"
calculate_percentage() {
local part="${1}"
local total="${2}"
local percentage
percentage=$(fltexpr )
}
calculate_percentage 75 200
POSIX Mode Improvements
Enhanced POSIX compliance:
set -o posix
[[ "ä" < "z" ]]
if [[ "${BASH_VERSINFO[0]}" -ge 5 ]] && [[ "${BASH_VERSINFO[1]}" -ge 3 ]]; then
USE_MODERN_FEATURES=1
else
set -o posix
USE_MODERN_FEATURES=0
fi
Improved Error Reporting
More detailed error messages:
pattern="[invalid"
if [[ "test" =~ ${pattern} ]]; then
echo "Match"
else
echo "No match" >&2
fi
validate_pattern() {
local pattern="${1}"
local test_string="test"
if [[ "${test_string}" =~ ${pattern} ]] 2>/dev/null; then
echo "Pattern is valid"
return 0
else
echo "Invalid regex pattern: ${pattern}" >&2
return 1
fi
}
validate_pattern "[a-z]+"
validate_pattern "[a-z"
C23 Compliance
Bash source updated to C23 standard:
- No longer compiles with K&R C compilers
- Modernized codebase
- 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() {
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:
for file in *; do
size=$(stat -f%z "${file}" 2>/dev/null)
done
for file in *; do
${|stat -f%z "${file}" 2>/dev/null;}
size="${REPLY}"
done
GLOBSORT="date:desc"
for file in *.log; do
process_recent_log "${file}"
done
Version Detection
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: