函数是组织代码、实现代码重用的重要手段。本章将详细介绍Shell脚本中函数的定义、调用、参数传递、返回值以及变量作用域,同时深入讲解函数库的设计、回调函数、递归函数等高级用法,帮助你全面掌握Shell函数的编写技巧。
6.1 函数的定义与调用
6.1.1 函数的基本语法
Shell脚本中定义函数有两种语法格式:
bash
# 语法1:使用 function 关键字(推荐,兼容性好)
function 函数名 {
命令
}
# 语法2:直接使用函数名(更简洁)
函数名() {
命令
}
6.1.2 函数的定义与调用示例
bash
#!/bin/bash
# 定义函数(语法1)
function greet {
echo "你好,欢迎学习Shell编程!"
}
# 定义函数(语法2)
say_hello() {
echo "Hello, World!"
}
# 调用函数
greet
say_hello
6.1.3 函数的返回值
Shell函数支持两种返回值方式:
bash
#!/bin/bash
# 方式1:使用 return 返回状态码(0-255)
# return 用于返回函数的退出状态,不是返回值本身
function check_file {
if [ -f "$1" ]; then
return 0 # 文件存在,返回成功
else
return 1 # 文件不存在,返回失败
fi
}
# 调用并检查返回值
check_file "/etc/passwd"
if [ $? -eq 0 ]; then
echo "文件存在"
else
echo "文件不存在"
fi
# 方式2:使用 echo 返回数据
# 通过捕获函数的输出来获取返回值
function get_date {
echo $(date +"%Y-%m-%d %H:%M:%S")
}
# 捕获函数输出
current_date=$(get_date)
echo "当前日期: $current_date"
# 方式3:返回计算结果
function add {
result=$(($1 + $2))
echo $result
}
sum=$(add 10 20)
echo "10 + 20 = $sum"
6.1.4 函数的递归调用
Shell支持函数递归调用:
bash
#!/bin/bash
# 递归计算阶乘
function factorial {
local n=$1
if [ $n -le 1 ]; then
echo 1
else
local prev=$(factorial $((n - 1)))
echo $((n * prev))
fi
}
# 测试递归
echo "5! = $(factorial 5)" # 输出:5! = 120
# 递归计算斐波那契数列
function fibonacci {
local n=$1
if [ $n -le 0 ]; then
echo 0
elif [ $n -eq 1 ]; then
echo 1
else
local a=$(fibonacci $((n - 1)))
local b=$(fibonacci $((n - 2)))
echo $((a + b))
fi
}
# 输出前10个斐波那契数
echo "斐波那契数列前10项:"
for i in {0..9}; do
echo -n "$(fibonacci $i) "
done
echo
6.2 函数参数传递
6.2.1 位置参数
Shell函数使用特殊变量来访问参数:
| 变量 | 描述 |
|---|---|
$0 |
脚本名称(函数中仍是脚本名) |
$1, $2, ... |
第1、2、...个参数 |
${10}, ${11}, ... |
第10、11、...个参数 |
$# |
参数个数 |
$* |
所有参数(作为单个字符串) |
$@ |
所有参数(作为独立字符串) |
$? |
上一个命令的退出状态 |
bash
#!/bin/bash
# 定义带参数的函数
function greet_user {
echo "你好,$1!"
echo "今天是 $2"
echo "你一共传入了 $# 个参数"
}
# 调用函数并传递参数
greet_user "张三" "2024-01-01"
# 参数编号超过9时需要使用 ${n}
function print_args {
echo "参数1: $1"
echo "参数2: $2"
echo "参数10: ${10}"
}
# 传递多个参数
print_args a b c d e f g h i j k l
6.2.2 $* 与 $@ 的区别
这两个变量在不加引号时没有区别,但在加引号时有重大区别:
bash
#!/bin/bash
function show_args {
echo "=== \$* 测试 ==="
for arg in "$*"; do
echo "参数: '$arg'"
done
echo ""
echo "=== \$@ 测试 ==="
for arg in "$@"; do
echo "参数: '$arg'"
done
}
# 调用函数
show_args "hello world" "foo bar" "test"
输出:
=== $* 测试 ===
参数: 'hello world foo bar test'
=== $@ 测试 ===
参数: 'hello world'
参数: 'foo bar'
参数: 'test'
结论:
"$*": 将所有参数合并为一个字符串"$@": 保留每个参数的独立性(推荐使用)
6.2.3 使用 shift 处理参数
shift 命令用于移动参数位置:
bash
#!/bin/bash
# 逐个处理参数
function process_args {
while [ $# -gt 0 ]; do
echo "处理参数: $1"
shift # 向左移动参数
done
}
# 调用函数
process_args arg1 arg2 arg3 arg4
# shift指定步长
function process_pairs {
while [ $# -ge 2 ]; do
echo "键: $1, 值: $2"
shift 2 # 一次移动两个位置
done
}
echo ""
echo "=== 处理键值对 ==="
process_pairs name=张三 age=25 city=北京
6.2.4 参数默认值与校验
bash
#!/bin/bash
# 参数默认值
function greet {
local name=${1:-"陌生人"} # 如果$1为空,使用默认值
echo "你好,$name!"
}
greet "李四" # 输出:你好,李四!
greet # 输出:你好,陌生人!
# 参数个数校验
function require_two_args {
if [ $# -lt 2 ]; then
echo "错误:需要至少2个参数,当前只有 $# 个"
return 1
fi
echo "参数1: $1"
echo "参数2: $2"
}
echo ""
require_two_args a # 输出:错误:需要至少2个参数,当前只有1个
require_two_args a b # 输出:参数1: a 参数2: b
# 参数类型校验
function require_number {
if ! [[ $1 =~ ^[0-9]+$ ]]; then
echo "错误:$1 不是有效数字"
return 1
fi
echo "数字验证通过: $1"
}
echo ""
require_number "123" # 输出:数字验证通过: 123
require_number "abc" # 输出:错误:abc 不是有效数字
6.3 函数返回值
6.3.1 return 与 echo 的选择
bash
#!/bin/bash
# return:返回状态码(0表示成功,1-255表示失败)
# 适合用于判断执行是否成功
function is_even {
if [ $(($1 % 2)) -eq 0 ]; then
return 0 # 是偶数,返回成功
else
return 1 # 是奇数,返回失败
fi
}
# 调用
number=4
is_even $number
if [ $? -eq 0 ]; then
echo "$number 是偶数"
else
echo "$number 是奇数"
fi
# echo:返回具体数据
# 适合用于返回计算结果或其他数据
function get_square {
echo $(($1 * $1))
}
square=$(get_square 5)
echo "5的平方是: $square"
6.3.2 综合示例:返回多种状态
bash
#!/bin/bash
# 函数返回多个值的技巧
# 方法1:使用全局变量
function get_user_info {
name="张三"
age=25
city="北京"
}
get_user_info
echo "姓名: $name, 年龄: $age, 城市: $city"
# 方法2:使用echo输出多个值,通过数组接收
function get_info {
echo "张三|25|北京"
}
# 方式2a:使用IFS分割
IFS='|' read -r name age city <<< $(get_info)
echo "方式2a - 姓名: $name, 年龄: $age, 城市: $city"
# 方式2b:直接读取命令输出
read name age city < <(get_info)
echo "方式2b - 姓名: $name, 年龄: $age, 城市: $city"
# 方法3:使用关联数组
function get_user_array {
declare -A info
info[name]="李四"
info[age]=30
info[city]="上海"
echo "${info[@]}" # 或者返回数组
}
# 调用并转换为关联数组
data=($(get_user_array))
echo "方式3 - 姓名: ${data[0]}, 年龄: ${data[1]}, 城市: ${data[2]}"
# 方法4:使用nameref( Bash 4.3+)
function fill_info {
local -n ref=$1 # nameref,引用传递
ref[name]="王五"
ref[age]=35
ref[city]="广州"
}
declare -A user_info
fill_info user_info
echo "方式4 - 姓名: ${user_info[name]}, 年龄: ${user_info[age]}, 城市: ${user_info[city]}"
6.4 变量作用域
6.4.1 全局变量与局部变量
bash
#!/bin/bash
# 全局变量(在函数外部定义,函数内部可直接访问)
global_var="我是全局变量"
# 局部变量(使用 local 关键字声明)
function test_local {
local local_var="我是局部变量"
echo "函数内部 - 全局变量: $global_var"
echo "函数内部 - 局部变量: $local_var"
}
# 调用函数
test_local
echo ""
echo "函数外部 - 全局变量: $global_var"
echo "函数外部 - 局部变量: $local_var" # 这里无法访问,会输出空
输出:
函数内部 - 全局变量: 我是全局变量
函数内部 - 局部变量: 我是局部变量
函数外部 - 全局变量: 我是全局变量
函数外部 - 局部变量:
6.4.2 使用局部变量的重要性
bash
#!/bin/bash
# 不使用局部变量的问题
counter=0
function increment_bad {
counter=$((counter + 1))
}
function increment_good {
local temp=$counter
temp=$((temp + 1))
counter=$temp
}
echo "初始值: $counter"
increment_bad
echo "increment_bad后: $counter"
counter=0 # 重置
increment_good
echo "increment_good后: $counter"
# 嵌套函数中的变量问题
function outer {
local x=10
function inner {
local x=20
echo "inner中x=$x"
}
inner
echo "outer中x=$x"
}
echo ""
outer
6.4.3 只读变量
使用 readonly 定义只读变量,防止被修改:
bash
#!/bin/bash
# 定义只读变量
readonly PI=3.14159
readonly MAX_COUNT=100
# 定义只读函数
function readonly_function {
echo "这是一个只读函数"
}
# 将函数设为只读(不可修改)
readonly -f readonly_function
# 查看所有只读变量
echo "=== 只读变量 ==="
readonly
# 查看只读函数
echo ""
echo "=== 只读函数 ==="
readonly -f
6.5 函数库与脚本引用
6.5.1 加载外部函数库
bash
#!/bin/bash
# 创建函数库文件 lib.sh
# 文件名:lib.sh
cat > lib.sh << 'EOF'
#!/bin/bash
# 常用函数库
# 打印带颜色的信息
print_info() {
echo -e "\033[32m[信息]\033[0m $1"
}
print_error() {
echo -e "\033[31m[错误]\033[0m $1"
}
print_warning() {
echo -e "\033[33m[警告]\033[0m $1"
}
# 检查命令是否存在
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# 获取脚本目录
get_script_dir() {
local source="${BASH_SOURCE[0]}"
while [ -h "$source" ]; do
local dir="$(cd -P "$(dirname "$source")" && pwd)"
source="$(readlink "$source")"
[[ $source != /* ]] && source="$dir/$source"
done
echo "$(cd -P "$(dirname "$source")" && pwd)"
}
EOF
# 在主脚本中加载函数库
source ./lib.sh
# 使用函数库中的函数
print_info "程序开始执行"
print_error "发生错误"
print_warning "警告信息"
if command_exists ls; then
print_info "ls命令存在"
fi
echo "脚本目录: $(get_script_dir)"
6.5.2 条件加载函数库
bash
#!/bin/bash
# 条件加载函数库
if [ -f "./common.sh" ]; then
source "./common.sh"
fi
# 或者指定默认路径
LIB_DIR="${HOME}/lib"
if [ -d "$LIB_DIR" ]; then
for lib in "$LIB_DIR"/*.sh; do
if [ -f "$lib" ]; then
source "$lib"
fi
done
fi
6.5.3 函数库的设计模式
bash
#!/bin/bash
# 函数库设计模式 - 防止重复加载
# 方法1:使用变量检测
[ -z "$_COMMON_LIB_LOADED" ] && {
# 定义库变量
_COMMON_LIB_LOADED=1
# 常用函数定义
function log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
}
function die() {
echo "[ERROR] $*" >&2
exit 1
}
function assert() {
if [ -z "$1" ]; then
die "断言失败: $2"
fi
}
} # 结束条件加载块
6.6 函数高级用法
6.6.1 回调函数
Shell不支持真正的回调函数,但可以通过函数指针模拟:
bash
#!/bin/bash
# 使用函数名作为参数实现回调
function execute_callback() {
local callback=$1
shift
$callback "$@"
}
function my_callback() {
echo "回调执行: $*"
}
execute_callback my_callback "参数1" "参数2"
# 更复杂的回调示例
function process_items() {
local callback=$1
shift
local items=("$@")
for item in "${items[@]}"; do
$callback "$item"
done
}
function print_item() {
echo "处理: $1"
}
function uppercase_item() {
echo "$1" | tr '[:lower:]' '[:upper:]'
}
echo "=== 回调示例1 ==="
process_items print_item apple banana cherry
echo ""
echo "=== 回调示例2 ==="
process_items uppercase_item hello world
6.6.2 匿名函数
bash
#!/bin/bash
# 使用 eval 创建匿名函数
eval "$(cat << 'EOF'
function anonymous {
echo "我是匿名函数: $*"
}
EOF
)"
anonymous "测试"
# 临时函数
(
function temp {
echo "临时函数,只在子shell中有效"
}
temp
)
echo "子shell外无法访问temp函数"
6.6.3 函数柯里化
bash
#!/bin/bash
# 柯里化示例 - 创建带预设参数的函数
function add() {
local a=$1
shift
local b=$1
echo $((a + b))
}
function multiply() {
local a=$1
shift
echo "local b=\$1; echo \$((a * b))"
}
# 使用柯里化
add5() {
local a=5
echo $(($1 + a))
}
multiply_by_3() {
echo $(($1 * 3))
}
echo "add5 10 = $(add5 10)"
echo "multiply_by_3 7 = $(multiply_by_3 7)"
6.6.4 动态函数定义
bash
#!/bin/bash
# 动态创建函数
create_greeting() {
local name=$1
eval "function hello_$name {
echo '你好,$name!'
}"
}
create_greeting "张三"
create_greeting "李四"
hello_张三
hello_李四
# 动态函数调用
function dispatcher() {
local cmd=$1
shift
if declare -f $cmd > /dev/null; then
$cmd "$@"
else
echo "未知命令: $cmd"
return 1
fi
}
function cmd1() {
echo "执行命令1: $*"
}
function cmd2() {
echo "执行命令2: $*"
}
dispatcher cmd1 "参数"
dispatcher cmd2 "参数"
6.7 综合示例
6.7.1 完整的工具函数库
bash
#!/bin/bash
#=========================================
# 常用Shell工具函数库
#=========================================
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# 日志函数
log_info() {
echo -e "${GREEN}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1" >&2
}
log_warning() {
echo -e "${YELLOW}[WARN]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}
log_debug() {
if [ "${DEBUG:-0}" = "1" ]; then
echo -e "${BLUE}[DEBUG]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
fi
}
# 确认函数
confirm() {
local prompt=$1
local default=${2:-"n"}
if [ "$default" = "y" ]; then
read -p "$prompt [Y/n]: " response
case $response in
[nN][oN]|[nN]) return 1 ;;
*) return 0 ;;
esac
else
read -p "$prompt [y/N]: " response
case $response in
[yY][eE][sS]|[yY]) return 0 ;;
*) return 1 ;;
esac
fi
}
# 创建备份
backup_file() {
local file=$1
if [ -f "$file" ]; then
local backup="${file}.$(date +%Y%m%d%H%M%S).bak"
cp "$file" "$backup"
log_info "已创建备份: $backup"
echo "$backup"
else
log_error "文件不存在: $file"
return 1
fi
}
# 检查依赖
check_dependency() {
local cmd=$1
if ! command -v "$cmd" >/dev/null 2>&1; then
log_error "缺少依赖: $cmd"
return 1
fi
return 0
}
# 进度显示
progress() {
local current=$1
local total=$2
local width=50
local percent=$((current * 100 / total))
local completed=$((width * current / total))
local remaining=$((width - completed))
printf "\r进度: ["
printf "%${completed}s" | tr ' ' '='
printf "%${remaining}s" | tr ' ' '-'
printf "] %3d%%" $percent
if [ $current -eq $total ]; then
echo ""
fi
}
# 输入验证
validate_number() {
[[ $1 =~ ^[0-9]+$ ]]
}
validate_integer() {
[[ $1 =~ ^-?[0-9]+$ ]]
}
validate_email() {
[[ $1 =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]
}
# 字符串处理
trim() {
local var="$*"
echo "${var#"${var%%[![:space:]]*}"}"
echo "${var%"${var##*[![:space:]]}"}"
}
to_upper() {
echo "$1" | tr '[:lower:]' '[:upper:]'
}
to_lower() {
echo "$1" | tr '[:upper:]' '[:lower:]'
}
# 数组操作
array_contains() {
local item=$1
shift
local arr=("$@")
for i in "${arr[@]}"; do
[ "$i" = "$item" ] && return 0
done
return 1
}
array_join() {
local delimiter=$1
shift
local arr=("$@")
local result=""
for i in "${!arr[@]}"; do
[ $i -eq 0 ] && result="${arr[$i]}" || result="$result$delimiter${arr[$i]}"
done
echo "$result"
}
# 文件操作
file_extension() {
local file=$1
echo "${file##*.}"
}
file_basename() {
local file=$1
echo "${file##*/}"
}
file_dirname() {
local file=$1
echo "${file%/*}"
}
# 测试工具函数库
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
log_info "这是工具函数库,请作为库引用使用"
# 测试confirm
echo "=== 测试confirm ==="
if confirm "确认继续?"; then
log_info "用户选择了确认"
else
log_info "用户选择了取消"
fi
# 测试依赖检查
echo ""
echo "=== 测试依赖检查 ==="
check_dependency "bash" && log_info "bash已安装"
check_dependency "nonexistent" || log_error "缺少依赖"
# 测试数组
echo ""
echo "=== 测试数组操作 ==="
arr=(apple banana cherry)
array_contains "banana" "${arr[@]}" && log_info "数组包含banana"
log_info "工具函数库测试完成"
fi
6.7.2 计算器函数
bash
#!/bin/bash
#=========================================
# 简单计算器函数
#=========================================
# 加法
add() {
local a=$1
local b=$2
echo $((a + b))
}
# 减法
subtract() {
local a=$1
local b=$2
echo $((a - b))
}
# 乘法
multiply() {
local a=$1
local b=$2
echo $((a * b))
}
# 除法
divide() {
local a=$1
local b=$2
if [ $b -eq 0 ]; then
echo "错误:除数不能为0" >&2
return 1
fi
echo $((a / b))
}
# 模运算
modulo() {
local a=$1
local b=$2
if [ $b -eq 0 ]; then
echo "错误:模数不能为0" >&2
return 1
fi
echo $((a % b))
}
# 乘方
power() {
local base=$1
local exp=$2
echo $((base ** exp))
}
# 平方根
sqrt() {
local n=$1
if [ $n -lt 0 ]; then
echo "错误:不能对负数求平方根" >&2
return 1
fi
local result=1
while true; do
result=$(((result + n / result) / 2))
if [ $((result * result)) -le $n ] && [ $(($((result + 1) * (result + 1)))) -gt $n ]; then
echo $result
return 0
fi
done
}
# 阶乘
factorial() {
local n=$1
if [ $n -lt 0 ]; then
echo "错误:不能对负数求阶乘" >&2
return 1
fi
local result=1
for ((i=2; i<=n; i++)); do
result=$((result * i))
done
echo $result
}
# 最大公约数
gcd() {
local a=$1
local b=$2
while [ $b -ne 0 ]; do
local temp=$b
b=$((a % b))
a=$temp
done
echo $a
}
# 最小公倍数
lcm() {
local a=$1
local b=$2
echo $((a * b / $(gcd $a $b)))
}
# 交互式计算器
calculator() {
while true; do
echo ""
echo "========== 计算器 =========="
echo "支持的运算: + - * / % ^"
echo "特殊功能: sqrt, factorial, gcd, lcm"
echo "输入 'quit' 退出"
echo "=============================="
read -p "请输入表达式: " expr
if [ "$expr" = "q" ] || [ "$expr" = "quit" ]; then
echo "退出计算器"
break
fi
# 解析表达式
local num1=$(echo "$expr" | awk '{print $1}')
local op=$(echo "$expr" | awk '{print $2}')
local num2=$(echo "$expr" | awk '{print $3}')
# 特殊函数
case $op in
sqrt)
result=$(sqrt $num1)
echo "sqrt($num1) = $result"
;;
factorial)
result=$(factorial $num1)
echo "$num1! = $result"
;;
gcd)
result=$(gcd $num1 $num2)
echo "gcd($num1, $num2) = $result"
;;
lcm)
result=$(lcm $num1 $num2)
echo "lcm($num1, $num2) = $result"
;;
+) result=$(add $num1 $num2) ;;
-) result=$(subtract $num1 $num2) ;;
x|X|\*) result=$(multiply $num1 $num2) ;;
/) result=$(divide $num1 $num2) ;;
%) result=$(modulo $num1 $num2) ;;
^) result=$(power $num1 $num2) ;;
*) echo "未知运算符: $op" ;;
esac
[ -n "$result" ] && echo "结果: $result"
done
}
# 测试
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
echo "=== 计算器测试 ==="
echo "10 + 5 = $(add 10 5)"
echo "10 - 5 = $(subtract 10 5)"
echo "10 * 5 = $(multiply 10 5)"
echo "10 / 5 = $(divide 10 5)"
echo "10 % 3 = $(modulo 10 3)"
echo "2 ^ 8 = $(power 2 8)"
echo "sqrt(16) = $(sqrt 16)"
echo "5! = $(factorial 5)"
echo "gcd(48, 18) = $(gcd 48 18)"
echo "lcm(4, 6) = $(lcm 4 6)"
echo ""
echo "=== 启动交互式计算器 ==="
calculator
fi
6.7.3 数据处理函数库
bash
#!/bin/bash
#=========================================
# 数据处理函数库
#=========================================
# 数组去重
array_unique() {
local arr=("$@")
echo "${arr[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '
}
# 数组反转
array_reverse() {
local arr=("$@")
local reversed=()
for ((i=${#arr[@]}-1; i>=0; i--)); do
reversed+=("${arr[$i]}")
done
echo "${reversed[@]}"
}
# 数组排序
array_sort() {
local arr=("$@")
echo "${arr[@]}" | tr ' ' '\n' | sort | tr '\n' ' '
}
# 数组最大值
array_max() {
local arr=("$@")
local max=${arr[0]}
for item in "${arr[@]}"; do
((item > max)) && max=$item
done
echo $max
}
# 数组最小值
array_min() {
local arr=("$@")
local min=${arr[0]}
for item in "${arr[@]}"; do
((item < min)) && min=$item
done
echo $min
}
# 数组求和
array_sum() {
local arr=("$@")
local sum=0
for item in "${arr[@]}"; do
sum=$((sum + item))
done
echo $sum
}
# 数组平均值
array_avg() {
local arr=("$@")
local sum=$(array_sum "${arr[@]}")
echo $((sum / ${#arr[@]}))
}
# 字符串包含判断
string_contains() {
local haystack=$1
local needle=$2
[[ "$haystack" == *"$needle"* ]]
}
# 字符串开头判断
string_starts_with() {
local str=$1
local prefix=$2
[[ "$str" == "$prefix"* ]]
}
# 字符串结尾判断
string_ends_with() {
local str=$1
local suffix=$2
[[ "$str" == *"$suffix" ]]
}
# 字符串替换(全局)
string_replace() {
local str=$1
local old=$2
local new=$3
echo "${str//$old/$new}"
}
# 文件读取为数组
file_to_array() {
local file=$1
local arr=()
while IFS= read -r line; do
arr+=("$line")
done < "$file"
echo "${arr[@]}"
}
# 数组写入文件
array_to_file() {
local file=$1
shift
local arr=("$@")
printf '%s\n' "${arr[@]}" > "$file"
}
# CSV解析
csv_get_field() {
local line=$1
local field=$2
local delimiter=${3:-","}
echo "$line" | cut -d"$delimiter" -f"$field"
}
# JSON解析(简单)
json_get_value() {
local json=$1
local key=$2
echo "$json" | grep -o "\"$key\":[^,}]*" | cut -d':' -f2 | tr -d ' "'
}
# URL编码
url_encode() {
local string=$1
echo -n "$string" | sed 's/ /%20/g; s/!/%21/g; s/#/%23/g; s/\$/%24/g; s/&/%26/g; s/+/%2B/g; s/,/%2C/g; s/\//%2F/g; s/:/%3A/g; s/;/%3B/g; s/=/%3D/g; s/?/%3F/g; s/@/%40/g; s/\[/%5B/g; s/\]/%5D/g'
}
# URL解码
url_decode() {
local string=$1
echo -n "$string" | sed 's/%20/ /g; s/%21/!/g; s/%23/#/g; s/%24/$/g; s/%26/\&/g; s/%2B/+/g; s/%2C/,/g; s/%2F/\//g; s/%3A/:/g; s/%3B/;/g; s/%3D/=/g; s/%3F/?/g; s/%40/@/g; s/%5B/[/g; s/%5D/]/g'
}
# 测试
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
echo "=== 数组函数测试 ==="
arr=(5 2 8 1 9 3)
echo "原数组: ${arr[@]}"
echo "去重后: $(array_unique 1 2 3 2 1 4)"
echo "反转后: $(array_reverse 1 2 3 4 5)"
echo "排序后: $(array_sort 5 2 8 1 9)"
echo "最大值: $(array_max 5 2 8 1 9)"
echo "最小值: $(array_min 5 2 8 1 9)"
echo "求和: $(array_sum 1 2 3 4 5)"
echo "平均值: $(array_avg 1 2 3 4 5)"
echo ""
echo "=== 字符串函数测试 ==="
str="Hello World"
string_contains "$str" "World" && echo "包含World"
string_starts_with "$str" "Hello" && echo "以Hello开头"
string_ends_with "$str" "World" && echo "以World结尾"
echo "替换后: $(string_replace "Hello" "Hi" "$str")"
echo ""
echo "=== URL编码测试 ==="
echo "编码: $(url_encode "hello world!")"
fi
6.8 本章小结
本章详细介绍了Shell脚本中函数的用法:
-
函数的定义与调用:
- 两种定义语法(
function关键字和直接函数名) - 函数的返回值(
returnvsecho) - 函数的递归调用
- 两种定义语法(
-
函数参数传递:
- 位置参数(
$1,$2, ...) $*与$@的区别(重点)- 使用
shift处理参数 - 参数默认值与校验
- 位置参数(
-
函数返回值:
return:返回状态码(0-255)echo:返回具体数据- 返回多个值的方法
-
变量作用域:
- 全局变量与局部变量的区别
- 使用
local声明局部变量 - 只读变量(
readonly)
-
函数库与脚本引用:
- 使用
source加载函数库 - 条件加载函数库
- 函数库设计模式
- 使用
-
函数高级用法:
- 回调函数模拟
- 匿名函数
- 函数柯里化
- 动态函数定义
-
综合示例:
- 完整的工具函数库(日志、确认、备份、输入验证、字符串处理、数组操作)
- 计算器函数(支持加减乘除模乘方阶乘最大公约数最小公倍数)
- 数据处理函数库(数组操作、字符串处理、文件操作、URL编解码)
掌握函数的使用可以大大提高脚本的模块化和可维护性,是编写大型Shell脚本的基础。
更多内容,欢迎访问南徽玉的个人博客