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)localfor all internal variables; (3) private functions prefixed with_; (4) consistent timestamped log format; (5) all errors written to stderr (>&2); (6) anenable_strict_modehelper 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 โ