Chapter 10

Functions and Arrays

Chapter 10: Shell Functions, Arrays, and String Processing

Functions are the foundation of script reuse, arrays enable bulk data processing, and advanced string operations let you say goodbye to awk/sed complexity. This chapter starts with function scope and return values, systematically covers indexed arrays, associative arrays, mapfile file reading, printf formatting, nameref, and closes with a complete production-grade function library.

10.1 Function Definition: Two Syntaxes

bash supports two function definition syntaxes that are functionally identical. The function keyword is a bash extension and not POSIX portable; the name() {} syntax works in all POSIX shells:

# ่ฏญๆณ•ไธ€๏ผšPOSIX ๅ…ผๅฎน๏ผˆๆŽจ่็”จไบŽ้œ€่ฆ็งปๆค็š„่„šๆœฌ๏ผ‰
greet() {
    echo "Hello, $1!"
}

# ่ฏญๆณ•ไบŒ๏ผšbash ๅ…ณ้”ฎๅญ—่ฏญๆณ•๏ผˆbash ไธ“ๅฑž๏ผ‰
function farewell {
    echo "Goodbye, $1!"
}

# ไธค็ง่ฏญๆณ•ๅฏไปฅๆทท็”จ๏ผŒ่กŒไธบๅฎŒๅ…จ็›ธๅŒ
greet "World"    # โ†’ Hello, World!
farewell "World" # โ†’ Goodbye, World!

# ๅ‡ฝๆ•ฐๅฟ…้กปๅ…ˆๅฎšไน‰ๅ†่ฐƒ็”จ๏ผˆbash ้กบๅบๆ‰ง่กŒ๏ผ‰
# ไพ‹ๅค–๏ผšๅฆ‚ๆžœๅ‡ฝๆ•ฐๅฎšไน‰ๅœจ source ็š„ๆ–‡ไปถไธญ๏ผŒ่ฐƒ็”จๆ—ถๅฟ…้กปๅ…ˆ source

# ๅ•่กŒๅ‡ฝๆ•ฐ๏ผˆ็”จๅˆ†ๅทๅˆ†้š”่ฏญๅฅ๏ผ‰
log() { echo "[$(date +%T)] $*"; }

# ๅ‡ฝๆ•ฐๅๅฏไปฅๅŒ…ๅซ่ฟžๅญ—็ฌฆ๏ผˆbash ็‰นๆ€ง๏ผ‰
check-root() {
    [[ $(id -u) -eq 0 ]] || { echo "้œ€่ฆ root ๆƒ้™" >&2; return 1; }
}

# ๆŸฅ็œ‹ๅทฒๅฎšไน‰็š„ๅ‡ฝๆ•ฐ
declare -F            # ๅˆ—ๅ‡บๆ‰€ๆœ‰ๅ‡ฝๆ•ฐๅ
declare -f greet      # ๆŸฅ็œ‹ greet ๅ‡ฝๆ•ฐ็š„ๅฎŒๆ•ดๅฎšไน‰
type greet            # ๆ˜พ็คบ greet ๆ˜ฏไธ€ไธช function

# ๅˆ ้™คๅ‡ฝๆ•ฐ
unset -f greet

10.2 Parameters and Return Values

Function parameters are accessed via positional variables $1~$9/${10}. return can only return an integer exit code (0-255). Two idiomatic patterns exist for returning string data:

#!/bin/bash

# === local ๅ˜้‡ไฝœ็”จๅŸŸ ===
# ๅ‡ฝๆ•ฐๅ†…ไธๅฃฐๆ˜Ž local ็š„ๅ˜้‡ๆ˜ฏๅ…จๅฑ€็š„๏ผ
x=10

modify_global() {
    x=99        # ไฟฎๆ”นไบ†ๅ…จๅฑ€ๅ˜้‡ x
    local y=42  # y ไป…ๅœจๅ‡ฝๆ•ฐๅ†…ๆœ‰ๆ•ˆ
}

modify_global
echo $x   # โ†’ 99๏ผˆๅ…จๅฑ€่ขซไฟฎๆ”น๏ผ‰
echo $y   # โ†’ ๏ผˆ็ฉบ๏ผŒy ๅœจๅ‡ฝๆ•ฐๅค–ไธๅญ˜ๅœจ๏ผ‰

# ่‰ฏๅฅฝๅฎž่ทต๏ผšๅ‡ฝๆ•ฐๅ†…ๆ‰€ๆœ‰ๅ˜้‡้ƒฝ็”จ local
calculate() {
    local a="$1"
    local b="$2"
    local result=$(( a + b ))
    echo $result   # ้€š่ฟ‡ stdout ่ฟ”ๅ›ž็ป“ๆžœ
}

# ๆ•่Žทๅ‡ฝๆ•ฐ่ฟ”ๅ›ž็š„ๅญ—็ฌฆไธฒ
sum=$(calculate 15 27)
echo "15 + 27 = $sum"   # โ†’ 15 + 27 = 42

# === return ้€€ๅ‡บ็  ===
is_even() {
    local n="$1"
    (( n % 2 == 0 ))   # ็ฎ—ๆœฏ่กจ่พพๅผ๏ผš0 ไธบ็œŸ(0)๏ผŒ้ž0 ไธบๅ‡(1)
    return $?
}

is_even 4 && echo "4 ๆ˜ฏๅถๆ•ฐ" || echo "4 ๆ˜ฏๅฅ‡ๆ•ฐ"
is_even 7 && echo "7 ๆ˜ฏๅถๆ•ฐ" || echo "7 ๆ˜ฏๅฅ‡ๆ•ฐ"

# === ้€š่ฟ‡ๅ…จๅฑ€ๅ˜้‡่ฟ”ๅ›žๅคๆ‚ๆ•ฐๆฎ ===
# ๆƒฏไพ‹๏ผš็”จ __ ๅ‰็ผ€็š„ๅ…จๅฑ€ๅ˜้‡ไฝœไธบ"่พ“ๅ‡บๅ‚ๆ•ฐ"
__split_result=()
split_string() {
    local str="$1"
    local sep="${2:- }"
    IFS="$sep" read -r -a __split_result 
  
## 10.3 Recursive Functions


  
```bash
#!/bin/bash

# ้˜ถไน˜๏ผˆ้€’ๅฝ’๏ผ‰
factorial() {
    local n="$1"
    if (( n 
  
## 10.4 Advanced Function Features


  
```bash
# ๅ‡ฝๆ•ฐไฝœไธบๅ‘ฝไปค๏ผˆ่ฆ†็›–ๅค–้ƒจๅ‘ฝไปค๏ผ‰
# ๆณจๆ„๏ผš่ฟ™ไผšๅฑ่”ฝๅŒๅ็š„ๅค–้ƒจๅ‘ฝไปค๏ผŒไฝฟ็”จๆ—ถ่ฆ่ฐจๆ…Ž
ls() {
    command ls --color=auto -F "$@"  # command ่ฐƒ็”จ็œŸๆญฃ็š„ ls
}

# source ๅฏผๅ…ฅๅ‡ฝๆ•ฐๅบ“
source /path/to/lib.sh   # ๅœจๅฝ“ๅ‰ Shell ไธญๆ‰ง่กŒ๏ผŒๅ…ฑไบซไฝœ็”จๅŸŸ
# ็ฎ€ๅ†™
. /path/to/lib.sh

# ๆฃ€ๆŸฅๅ‡ฝๆ•ฐๆ˜ฏๅฆๅทฒๅฎšไน‰
if declare -f my_func &>/dev/null; then
    echo "my_func ๅทฒๅฎšไน‰"
    my_func
fi

# ๅˆ—ๅ‡บๆ‰€ๆœ‰ๅ‡ฝๆ•ฐ
declare -F | awk '{print $3}'  # ๅชๆ‰“ๅฐๅ‡ฝๆ•ฐๅ

# ๅˆ ้™คๅ‡ฝๆ•ฐ
unset -f ls   # ๆขๅค ls ไธบๅค–้ƒจๅ‘ฝไปค

# trap โ€” ๆ•่Žทไฟกๅท๏ผŒๅธธ็”จไบŽๅ‡ฝๆ•ฐไธญ็š„ๆธ…็†ๆ“ไฝœ
cleanup() {
    echo "ๆธ…็†ไธดๆ—ถๆ–‡ไปถ..."
    rm -f /tmp/myapp.$$.*
}
trap cleanup EXIT   # ่„šๆœฌ้€€ๅ‡บๆ—ถ่‡ชๅŠจ่ฐƒ็”จ cleanup
trap cleanup INT    # Ctrl+C ๆ—ถไนŸ่ฐƒ็”จ

# FUNCNAME ๆ•ฐ็ป„ โ€” ๅ‡ฝๆ•ฐ่ฐƒ็”จๆ ˆ
outer() {
    inner
}
inner() {
    echo "่ฐƒ็”จๆ ˆ: ${FUNCNAME[*]}"  # โ†’ inner outer main
    echo "ๅฝ“ๅ‰ๅ‡ฝๆ•ฐ: ${FUNCNAME[0]}" # โ†’ inner
    echo "่ฐƒ็”จ่€…: ${FUNCNAME[1]}"   # โ†’ outer
}
outer

10.5 Indexed Arrays (declare -a)

#!/bin/bash

# === ๅˆ›ๅปบๆ•ฐ็ป„ ===
fruits=("apple" "banana" "cherry")           # ็›ดๆŽฅ่ต‹ๅ€ผ
declare -a colors=("red" "green" "blue")     # ๆ˜พๅผๅฃฐๆ˜Ž
nums=(1 2 3 4 5)
mixed=("hello" 42 "world" 3.14)             # ็ฑปๅž‹ๆททๅˆ๏ผˆๅ…จไธบๅญ—็ฌฆไธฒ๏ผ‰

# ๆŒ‰็ดขๅผ•่ต‹ๅ€ผ๏ผˆๅฏไปฅไธ่ฟž็ปญ๏ผ‰
arr[0]="first"
arr[1]="second"
arr[5]="sixth"   # ไธญ้—ด็ฉบๆดž๏ผˆ3,4 ไธๅญ˜ๅœจ๏ผ‰

# === ่ฎฟ้—ฎๅ…ƒ็ด  ===
echo ${fruits[0]}     # โ†’ apple๏ผˆ็ฌฌไธ€ไธช๏ผ‰
echo ${fruits[1]}     # โ†’ banana
echo ${fruits[-1]}    # โ†’ cherry๏ผˆๆœ€ๅŽไธ€ไธช๏ผŒbash 4.3+๏ผ‰
echo ${fruits[-2]}    # โ†’ banana๏ผˆๅ€’ๆ•ฐ็ฌฌไบŒไธช๏ผ‰

# === ๆ•ดไฝ“ๆ“ไฝœ ===
echo ${fruits[@]}     # โ†’ apple banana cherry๏ผˆๆ‰€ๆœ‰ๅ…ƒ็ด ๏ผ‰
echo ${fruits[*]}     # โ†’ apple banana cherry๏ผˆๅŒไธŠ๏ผŒๅœจๅŒๅผ•ๅทๅ†…่กŒไธบไธๅŒ๏ผ‰
echo ${#fruits[@]}    # โ†’ 3๏ผˆๅ…ƒ็ด ไธชๆ•ฐ๏ผ‰
echo ${!fruits[@]}    # โ†’ 0 1 2๏ผˆๆ‰€ๆœ‰็ดขๅผ•๏ผ‰

# $@ ไธŽ $* ๅœจๅผ•ๅทๅ†…็š„ๅŒบๅˆซ๏ผˆๆ•ฐ็ป„ๅŒ็†๏ผ‰
for item in "${fruits[@]}"; do echo "$item"; done  # ๆฏไธชๅ…ƒ็ด ็‹ฌ็ซ‹
for item in "${fruits[*]}"; do echo "$item"; done  # ๅˆๅนถไธบไธ€ไธชๅญ—็ฌฆไธฒ

# === ๅขžๅˆ ๆ”น ===
fruits+=("date" "elderberry")  # ่ฟฝๅŠ ๅ…ƒ็ด 
fruits[1]="BANANA"             # ไฟฎๆ”นๅ…ƒ็ด 
unset fruits[2]                # ๅˆ ้™คๅ…ƒ็ด ๏ผˆ็•™ไธ‹็ฉบๆดž๏ผŒ็ดขๅผ•ไธ้‡ๆŽ’๏ผ‰
echo ${fruits[@]}              # โ†’ apple BANANA date elderberry

# ๅˆ ้™คๆ•ดไธชๆ•ฐ็ป„
# unset fruits

# === ๆ•ฐ็ป„ๅˆ‡็‰‡ ===
echo ${fruits[@]:1:2}   # ไปŽ็ดขๅผ•1ๅผ€ๅง‹ๅ–2ไธชๅ…ƒ็ด 

# === ้ๅކ๏ผˆๅธฆ็ดขๅผ•๏ผ‰===
for i in "${!fruits[@]}"; do
    echo "fruits[$i] = ${fruits[$i]}"
done

# === ๆ•ฐ็ป„ๆŽ’ๅบ ===
unsorted=(banana apple cherry date elderberry)
IFS=$'\n' sorted=($(sort 
  
## 10.6 Associative Arrays (declare -A) โ€” bash 4+


  
```bash
#!/bin/bash
# ๅ…ณ่”ๆ•ฐ็ป„๏ผˆๅญ—ๅ…ธ๏ผ‰๏ผŒbash 4.0+ ๆ‰ๆ”ฏๆŒ
# ๆฃ€ๆŸฅ็‰ˆๆœฌ๏ผš[[ ${BASH_VERSINFO[0]} -ge 4 ]] || { echo "้œ€่ฆ bash 4+"; exit 1; }

# === ๅˆ›ๅปบๅ…ณ่”ๆ•ฐ็ป„ ===
declare -A user_info
user_info[name]="Alice"
user_info[age]="30"
user_info[role]="admin"

# ไธ€ๆฌกๆ€งๅˆๅง‹ๅŒ–๏ผˆbash 4+๏ผ‰
declare -A config=(
    [host]="localhost"
    [port]="5432"
    [dbname]="myapp"
    [user]="dbuser"
)

# === ่ฎฟ้—ฎ ===
echo ${user_info[name]}     # โ†’ Alice
echo ${config[port]}        # โ†’ 5432

# === ๆ•ดไฝ“ๆ“ไฝœ ===
echo ${config[@]}           # ๆ‰€ๆœ‰ๅ€ผ๏ผˆ้กบๅบไธไฟ่ฏ๏ผ‰
echo ${!config[@]}          # ๆ‰€ๆœ‰้”ฎ
echo ${#config[@]}          # ้”ฎๅ€ผๅฏนๆ•ฐ้‡

# === ้ๅކ้”ฎๅ€ผๅฏน ===
for key in "${!config[@]}"; do
    echo "$key = ${config[$key]}"
done

# === ๆฃ€ๆŸฅ้”ฎๆ˜ฏๅฆๅญ˜ๅœจ ===
key="host"
if [[ -v config[$key] ]]; then   # -v ๆฃ€ๆŸฅๅ˜้‡ๆ˜ฏๅฆๅทฒ่ฎพ็ฝฎ๏ผˆbash 4.2+๏ผ‰
    echo "้”ฎ '$key' ๅญ˜ๅœจ: ${config[$key]}"
fi

# ๅ…ผๅฎนๆ€งๅ†™ๆณ•๏ผˆbash 4.0๏ผ‰
if [[ ${config[$key]+_} ]]; then
    echo "้”ฎ '$key' ๅญ˜ๅœจ"
fi

# === ๅˆ ้™ค้”ฎ ===
unset config[user]
echo ${#config[@]}    # โ†’ 3

# === ๅฎžๆˆ˜๏ผš็ปŸ่ฎกๅ•่ฏ้ข‘็އ ===
declare -A word_count
text="the quick brown fox jumps over the lazy dog the fox"
for word in $text; do
    (( word_count[$word]++ ))
done

# ๆŒ‰้ข‘็އ้™ๅบ่พ“ๅ‡บ
for word in "${!word_count[@]}"; do
    echo "${word_count[$word]} $word"
done | sort -rn

# === ๅฎžๆˆ˜๏ผš่งฃๆž้…็ฝฎๆ–‡ไปถ ===
declare -A cfg
while IFS='=' read -r key value; do
    [[ $key =~ ^[[:space:]]*# ]] && continue  # ่ทณ่ฟ‡ๆณจ้‡Š
    [[ -z $key ]] && continue                  # ่ทณ่ฟ‡็ฉบ่กŒ
    cfg[${key// /}]="${value// /}"             # ๅŽป้™ค็ฉบๆ ผ
done 
  
## 10.7 mapfile / readarray: Reading Files into Arrays


  
```bash
#!/bin/bash

# mapfile๏ผˆbash 4+๏ผŒreadarray ๆ˜ฏๅ…ถๅˆซๅ๏ผ‰
# ๅฐ†ๆ–‡ไปถ็š„ๆฏไธ€่กŒ่ฏปๅ…ฅๆ•ฐ็ป„๏ผŒ-t ๅŽปๆމๆœซๅฐพๆข่กŒ็ฌฆ๏ผˆๅ‡ ไนŽๆ€ปๆ˜ฏ้œ€่ฆ๏ผ‰

mapfile -t lines  **Version Note:**          Both `mapfile` and associative arrays (`declare -A`) require bash 4.0+. macOS ships with bash 3.2 (GPL licensing). Install a newer version with `brew install bash`, or check the version at the top of your script: `[[ ${BASH_VERSINFO[0]} -ge 4 ]] || exit 1`.


  
  
## 10.8 Formatted Output with printf


  
```bash
#!/bin/bash

# printf ๆฏ” echo ๆ›ดๅฏๆŽง๏ผŒ่กŒไธบไธŽ C ็š„ printf ไธ€่‡ด

# ๅŸบๆœฌๆ ผๅผ็ฌฆ
printf "%s\n" "hello"           # ๅญ—็ฌฆไธฒ
printf "%d\n" 42                # ๆ•ดๆ•ฐ
printf "%f\n" 3.14              # ๆตฎ็‚น๏ผˆ้ป˜่ฎค6ไฝๅฐๆ•ฐ๏ผ‰
printf "%.2f\n" 3.14159         # ไฟ็•™2ไฝๅฐๆ•ฐ
printf "%x\n" 255               # ๅๅ…ญ่ฟ›ๅˆถ๏ผˆff๏ผ‰
printf "%X\n" 255               # ๅๅ…ญ่ฟ›ๅˆถๅคงๅ†™๏ผˆFF๏ผ‰
printf "%o\n" 8                 # ๅ…ซ่ฟ›ๅˆถ๏ผˆ10๏ผ‰
printf "%e\n" 12345.678         # ็ง‘ๅญฆ่ฎกๆ•ฐๆณ•
printf "%b\n" "hello\nworld"    # ่งฃ้‡Š่ฝฌไน‰ๅบๅˆ—

# ๅฎฝๅบฆไธŽๅฏน้ฝ
printf "%-20s %5d\n" "Alice"  30   # ๅทฆๅฏน้ฝๅญ—็ฌฆไธฒ๏ผŒๅณๅฏน้ฝๆ•ฐๅญ—
printf "%-20s %5d\n" "Bob"    25
printf "%-20s %5d\n" "Charlie" 28
# ่พ“ๅ‡บ๏ผš
# Alice                   30
# Bob                     25
# Charlie                 28

# ่กฅ้›ถ
printf "%05d\n" 42      # โ†’ 00042
printf "%08.2f\n" 3.14  # โ†’ 00003.14

# ๅคšๆฌก้‡ๅคๆ ผๅผ๏ผˆๅ‚ๆ•ฐๅคšไบŽๅ ไฝ็ฌฆๆ—ถ๏ผŒๆ ผๅผไผš้‡ๅค๏ผ‰
printf "%s\n" apple banana cherry
# โ†’ apple
#    banana
#    cherry

# ้ขœ่‰ฒ่พ“ๅ‡บ๏ผˆANSI ่ฝฌไน‰็ ๏ผ‰
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
BOLD='\033[1m'
RESET='\033[0m'

printf "${RED}้”™่ฏฏ๏ผš${RESET}ๆ–‡ไปถไธๅญ˜ๅœจ\n"
printf "${GREEN}ๆˆๅŠŸ๏ผš${RESET}้ƒจ็ฝฒๅฎŒๆˆ\n"
printf "${YELLOW}่ญฆๅ‘Š๏ผš${RESET}็ฃ็›˜็ฉบ้—ดไธ่ถณ\n"
printf "${BOLD}${BLUE}ไฟกๆฏ๏ผš${RESET}ๆœๅŠกๅทฒๅฏๅŠจ\n"

# ๅ†™ๅ…ฅๆ–‡ไปถ๏ผˆไธ็”จ echo ๅ’Œ้‡ๅฎšๅ‘๏ผ‰
printf "Name: %s\nAge: %d\n" "Alice" 30 > /tmp/user.txt

# ็”Ÿๆˆๅ›บๅฎšๅฎฝๅบฆ็š„่กจๆ ผ
printf "%-15s %-10s %-10s\n" "Name" "Status" "PID"
printf "%-15s %-10s %-10s\n" "nginx" "running" "1234"
printf "%-15s %-10s %-10s\n" "mysql" "stopped" "โ€”"

# ่ฟ›ๅบฆๆก
progress_bar() {
    local current="$1"
    local total="$2"
    local width=40
    local pct=$(( current * 100 / total ))
    local filled=$(( current * width / total ))
    local empty=$(( width - filled ))
    printf "\r[%-*s] %3d%%" $width "$(printf '#%.0s' $(seq 1 $filled))" $pct
    [[ $current -eq $total ]] && echo
}

for i in $(seq 1 20); do
    progress_bar $i 20
    sleep 0.05
done

10.9 Advanced String Processing

#!/bin/bash

# === ๆญฃๅˆ™ๅŒน้…ไธŽ BASH_REMATCH ===
email="[email protected]"
if [[ $email =~ ^([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+)\.([a-zA-Z]{2,})$ ]]; then
    echo "ๅˆๆณ•้‚ฎ็ฎฑ"
    echo "็”จๆˆทๅ: ${BASH_REMATCH[1]}"   # โ†’ user
    echo "ๅŸŸๅ: ${BASH_REMATCH[2]}"     # โ†’ example
    echo "้กถ็บงๅŸŸ: ${BASH_REMATCH[3]}"   # โ†’ com
fi

# ๆๅ– IP ๅœฐๅ€
text="ๆœๅŠกๅ™จๅœฐๅ€๏ผš192.168.1.100๏ผŒๅค‡็”จ๏ผš10.0.0.1"
if [[ $text =~ ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+) ]]; then
    echo "็ฌฌไธ€ไธช IP: ${BASH_REMATCH[1]}"   # โ†’ 192.168.1.100
fi

# === ๅคงๅฐๅ†™่ฝฌๆข๏ผˆbash 4+๏ผ‰===
str="Hello World"
echo ${str,,}    # โ†’ hello world๏ผˆๅ…จๅฐๅ†™๏ผ‰
echo ${str^^}    # โ†’ HELLO WORLD๏ผˆๅ…จๅคงๅ†™๏ผ‰
echo ${str,}     # โ†’ hELLO wORLD๏ผˆไป…้ฆ–ๅญ—ๆฏๅฐๅ†™๏ผŒ่‹ฅๅŽŸไธบๅคงๅ†™๏ผ‰
echo ${str^}     # โ†’ Hello World๏ผˆไป…้ฆ–ๅญ—ๆฏๅคงๅ†™๏ผ‰

# ้€ๅญ—่ฝฌๆข๏ผˆๆŒ‡ๅฎšๅญ—็ฌฆ๏ผ‰
echo ${str^^[aeiou]}   # โ†’ HEllO WOrld๏ผˆๅ…ƒ้Ÿณ่ฝฌๅคงๅ†™๏ผ‰

# === ๅŽป้™ค้ฆ–ๅฐพ็ฉบ็™ฝ๏ผˆbash ๆ— ๅ†…็ฝฎ๏ผŒ็”จๅ‚ๆ•ฐๅฑ•ๅผ€ๆจกๆ‹Ÿ๏ผ‰===
trim() {
    local str="$1"
    # ๅŽป้™คๅ‰ๅฏผ็ฉบ็™ฝ
    str="${str#"${str%%[![:space:]]*}"}"
    # ๅŽป้™คๅฐพ้ƒจ็ฉบ็™ฝ
    str="${str%"${str##*[![:space:]]}"}"
    echo "$str"
}
result=$(trim "   hello world   ")
echo "[$result]"   # โ†’ [hello world]

# ๆ›ด็ฎ€ๆด็š„ๅ†™ๆณ•๏ผˆไฝฟ็”จๆญฃๅˆ™๏ผ‰
trim2() {
    local str="$*"
    [[ $str =~ ^[[:space:]]*(.*[^[:space:]])[[:space:]]*$ ]] && echo "${BASH_REMATCH[1]}" || echo ""
}

# === ๅญ—็ฌฆไธฒ้‡ๅค ===
repeat() {
    local str="$1"
    local n="$2"
    printf "%${n}s" | tr ' ' "$str"
}
repeat '-' 40   # โ†’ ----------------------------------------

# === ๅญ—็ฌฆไธฒๅŒ…ๅซๆฃ€ๆต‹ ===
haystack="Hello, World!"
needle="World"
if [[ $haystack == *"$needle"* ]]; then
    echo "ๅŒ…ๅซ '$needle'"
fi

# === ๅญ—็ฌฆไธฒๅˆ†ๅ‰ฒ ===
csv="a,b,c,d,e"
IFS=',' read -r -a parts 
  
## 10.10 Name References (declare -n)


  
`declare -n` (nameref, bash 4.3+) creates a variable whose value is the name of another variable. Through nameref, functions can return arrays and other complex data structures:


  
```bash
#!/bin/bash

# ๅŸบๆœฌ nameref
real_var="Hello"
declare -n alias=real_var   # alias ๆ˜ฏ real_var ็š„ๅˆซๅ
echo "$alias"               # โ†’ Hello๏ผˆ่ฎฟ้—ฎ real_var ็š„ๅ€ผ๏ผ‰
alias="World"               # ้€š่ฟ‡ alias ไฟฎๆ”น real_var
echo "$real_var"            # โ†’ World

# === ๅ‡ฝๆ•ฐ้€š่ฟ‡ nameref ่ฟ”ๅ›žๆ•ฐ็ป„ ===
# ่ฟ™ๆ˜ฏ bash ่ฟ”ๅ›žๆ•ฐ็ป„็š„ๆœ€ไฝณๅฎž่ทต๏ผˆbash 4.3+๏ผ‰

get_users() {
    declare -n _result="$1"   # $1 ๆ˜ฏ่ฐƒ็”จ่€…ไผ ๅ…ฅ็š„ๆ•ฐ็ป„ๅ
    _result=()
    while IFS=: read -r name _ uid _; do
        (( uid >= 1000 )) && _result+=("$name")
    done 
  
## 10.11 Function Library Design: Complete Example


  
Below is a complete production-grade shell function library, including logging, colored output, error handling, and duplicate-load prevention:


  
```bash
#!/bin/bash
# lib/common.sh โ€” ้€š็”จๅ‡ฝๆ•ฐๅบ“
# ไฝฟ็”จๆ–นๆณ•๏ผšsource lib/common.sh

# ============================================================
# ้˜ฒ้‡ๅคๅŠ ่ฝฝ๏ผˆๅน‚็ญ‰ source๏ผ‰
# ============================================================
[[ -n "${__COMMON_LIB_LOADED:-}" ]] && return 0
readonly __COMMON_LIB_LOADED=1

# ============================================================
# ้ขœ่‰ฒๅธธ้‡
# ============================================================
readonly _CLR_RESET='\033[0m'
readonly _CLR_RED='\033[0;31m'
readonly _CLR_GREEN='\033[0;32m'
readonly _CLR_YELLOW='\033[1;33m'
readonly _CLR_BLUE='\033[0;34m'
readonly _CLR_CYAN='\033[0;36m'
readonly _CLR_BOLD='\033[1m'

# ============================================================
# ๆ—ฅๅฟ—ๅ‡ฝๆ•ฐ
# ============================================================

# ๅ†…้ƒจ๏ผšๅธฆๆ—ถ้—ดๆˆณ่พ“ๅ‡บ
_log() {
    local level="$1"
    local color="$2"
    shift 2
    local msg="$*"
    local timestamp
    timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    printf "${color}[${timestamp}] [${level}]${_CLR_RESET} %s\n" "$msg" >&2
}

# ๅ…ฌๅ…ฑๆ—ฅๅฟ—ๅ‡ฝๆ•ฐ
log_info()    { _log "INFO " "${_CLR_GREEN}"  "$@"; }
log_warn()    { _log "WARN " "${_CLR_YELLOW}" "$@"; }
log_error()   { _log "ERROR" "${_CLR_RED}"    "$@"; }
log_debug()   {
    [[ "${LOG_LEVEL:-info}" == "debug" ]] || return 0
    _log "DEBUG" "${_CLR_CYAN}" "$@"
}

# ่‡ดๅ‘ฝ้”™่ฏฏ๏ผšๆ‰“ๅฐๅŽ้€€ๅ‡บ
log_fatal() {
    _log "FATAL" "${_CLR_RED}${_CLR_BOLD}" "$@"
    exit 1
}

# ============================================================
# ้”™่ฏฏๅค„็†
# ============================================================

# ่ฎพ็ฝฎ้”™่ฏฏๅค„็†ๆจกๅผ๏ผˆๅœจไธป่„šๆœฌ่ฐƒ็”จ๏ผ‰
enable_strict_mode() {
    set -euo pipefail
    trap '_handle_error ${LINENO} "$BASH_COMMAND"' ERR
}

_handle_error() {
    local line="$1"
    local cmd="$2"
    log_error "ๅ‘ฝไปคๅคฑ่ดฅ๏ผˆ็ฌฌ ${line} ่กŒ๏ผ‰: ${cmd}"
    log_error "้€€ๅ‡บ็ : $?"
}

# ============================================================
# ๅทฅๅ…ทๅ‡ฝๆ•ฐ
# ============================================================

# ๆฃ€ๆŸฅๅ‘ฝไปคๆ˜ฏๅฆๅญ˜ๅœจ
require_cmd() {
    local cmd
    for cmd in "$@"; do
        command -v "$cmd" &>/dev/null || log_fatal "ๅฟ…้กปๅฎ‰่ฃ…ๅ‘ฝไปค: ${cmd}"
    done
}

# ๆฃ€ๆŸฅๆ˜ฏๅฆไปฅ root ่ฟ่กŒ
require_root() {
    [[ $(id -u) -eq 0 ]] || log_fatal "ๆญค่„šๆœฌๅฟ…้กปไปฅ root ่ฟ่กŒ"
}

# ๅฎ‰ๅ…จๅˆ›ๅปบ็›ฎๅฝ•
ensure_dir() {
    local dir="$1"
    local mode="${2:-755}"
    if [[ ! -d "$dir" ]]; then
        mkdir -p "$dir" && chmod "$mode" "$dir" || log_fatal "ๆ— ๆณ•ๅˆ›ๅปบ็›ฎๅฝ•: ${dir}"
        log_info "ๅˆ›ๅปบ็›ฎๅฝ•: ${dir}"
    fi
}

# ็กฎ่ฎคๆ็คบ๏ผˆ่ฟ”ๅ›ž 0 ็กฎ่ฎค๏ผŒ1 ๅ–ๆถˆ๏ผ‰
confirm() {
    local msg="${1:-็กฎ่ฎค็ปง็ปญ๏ผŸ}"
    local answer
    read -r -p "${msg} [y/N] " answer
    [[ ${answer,,} == "y" ]]
}

# ้‡่ฏ•ๅ‡ฝๆ•ฐ๏ผˆๆœ€ๅคš N ๆฌก๏ผ‰
retry() {
    local max_attempts="${1:-3}"
    local delay="${2:-2}"
    shift 2
    local attempt=1
    while (( attempt  backup.sql
#     log_info "ๅค‡ไปฝๅฎŒๆˆ๏ผš$(format_size $(stat -c%s backup.sql))"
# fi
#
# log_info "้ƒจ็ฝฒๅฎŒๆˆ"

Function Library Best Practices: A good shell function library should have: (1) duplicate-load guard (__LIB_LOADED); (2) local for all internal variables; (3) private functions prefixed with _; (4) consistent timestamped log format; (5) all errors written to stderr (>&2); (6) an enable_strict_mode helper for callers to opt in to strict mode.

Chapter Summary: This chapter fully covered the bash function system (definition/scope/recursion/nameref), all operations on indexed and associative arrays, efficient file reading with mapfile, printf formatting and color output, advanced string processing, and a production-ready function library template. The next chapter dives into pipes, process substitution, and the underlying mechanics of file descriptors.

  Previous
  โ† Ch9: Variables


  Next
  Ch11: Pipes โ†’
Rate this chapter
4.5  / 5  (31 ratings)

๐Ÿ’ฌ Comments