1️⃣ 函数基础
函数定义与调用
#!/bin/bash # 定义方式一(推荐,兼容性好) greet() { echo "你好!" } # 定义方式二(function 关键字) function hello() { echo "Hello, World!" } # 调用函数(不加括号!) greet hello # 函数必须在调用前定义(Shell 顺序执行) bash
函数参数
#!/bin/bash check_service() { local svc_name="$1" # 函数内的位置参数 if systemctl is-active "$svc_name" >/dev/null 2>&1; then echo "✓ $svc_name 运行中" else echo "✗ $svc_name 已停止" fi } # 调用时传参 check_service "sshd" check_service "nginx" # 函数内 $1 $2 $@ $# 表示函数的参数,不是脚本的参数 bash
函数返回值
#!/bin/bash # return 只能返回数字(0-255),0=成功,非0=失败 is_root() { [[ $EUID -eq 0 ]] && return 0 || return 1 } if is_root; then echo "当前是 root 用户" else echo "非 root 用户" fi # 返回字符串:通过 echo 输出 + 命令替换捕获 get_ip() { hostname -I | awk '{print $1}' } my_ip=$( get_ip ) echo "本机IP:$my_ip" # 获取上一个函数的返回码 some_function echo "返回码:$?" bash
local 局部变量
#!/bin/bash name="全局变量" test_scope() { local name="局部变量" # local 声明只在函数内有效 echo "函数内:$name" # 局部变量 } test_scope echo "函数外:$name" # 全局变量,不受影响 bash
最佳实践在函数内所有变量都用
local 声明,避免污染全局变量空间,提高脚本健壮性。2️⃣ 索引数组
#!/bin/bash # 定义数组(索引从0开始) fruits=("苹果" "香蕉" "橙子" "葡萄") # 访问元素 echo ${fruits[0]} # 苹果 echo ${fruits[2]} # 橙子 echo ${fruits[-1]} # 葡萄(最后一个) # 所有元素 echo ${fruits[@]} # 苹果 香蕉 橙子 葡萄 # 数组长度 echo ${#fruits[@]} # 4 # 所有索引 echo ${!fruits[@]} # 0 1 2 3 # 修改元素 fruits[1]="芒果" # 追加元素 fruits+=("草莓") fruits[${#fruits[@]}]="西瓜" # 删除元素 unset fruits[1] # 切片:${数组名[@]:起始:长度} echo ${fruits[@]:1:2} # 取第1到第2个元素 # 遍历数组 for fruit in "${fruits[@]}"; do echo "- $fruit" done bash
3️⃣ 关联数组(字典/哈希表)
#!/bin/bash # 需要 Bash 4+,必须先声明 declare -A config # 赋值 config[host]="192.168.1.100" config[port]="5432" config[user]="postgres" config[db]="myapp" # 也可以直接初始化 declare -A server=([ip]="10.0.0.1" [name]="web01" [cpu]="8核") # 访问 echo "DB 主机:${config[host]}" echo "DB 端口:${config[port]}" # 所有 key echo "Keys: ${!config[@]}" # 所有 value echo "Values: ${config[@]}" # 遍历 key-value for key in "${!config[@]}"; do printf " %-10s = %s\n" "$key" "${config[$key]}" done # 判断 key 是否存在 [[ -v config[host] ]] && echo "host 键存在" bash
4️⃣ 算术运算
| 方式 | 语法 | 说明 |
|---|---|---|
$(( )) | result=$((a+b)) | 算术扩展,最推荐 |
(( )) | ((a++)) | 算术命令,可修改变量 |
let | let "a=b+c" | 旧式写法 |
expr | expr $a + $b | 外部命令,最慢,不推荐 |
bc | echo "1.5+2.3" | bc | 浮点运算 |
#!/bin/bash a=10 b=3 # 基本运算 echo "加法:$((a + b))" # 13 echo "减法:$((a - b))" # 7 echo "乘法:$((a * b))" # 30 echo "除法:$((a / b))" # 3(整除) echo "取余:$((a % b))" # 1 echo "幂运算:$((2 ** 10))" # 1024 # 自增/自减 (( a++ )) # a = 11 (( a-- )) # a = 10 (( a += 5 )) # a = 15 (( a *= 2 )) # a = 30 # 浮点运算(需要 bc) result=$(echo "scale=2; 10 / 3" | bc) # 3.33 echo "10 / 3 = $result" # 随机数 echo "1-100 随机数:$((RANDOM % 100 + 1))" # 进制转换 echo "十进制 255 = 十六进制 $(printf '%x\n' 255)" echo "十六进制 ff = 十进制 $((16#ff))" bash
5️⃣ 函数库模式
将常用函数封装到库文件,供多个脚本复用:
#!/bin/bash # 文件:lib/common.sh 函数库 # 彩色输出函数 log_info() { echo "\e[32m[INFO]\e[0m $*"; } log_warn() { echo "\e[33m[WARN]\e[0m $*"; } log_error() { echo "\e[31m[ERROR]\e[0m $*" >&2; } log_success() { echo "\e[32m[OK]\e[0m $*"; } # 检查命令是否存在 require_cmd() { if ! command -v "$1" >/dev/null 2>&1; then log_error "命令 '$1' 未安装" exit 1 fi } # 检查是否为 root require_root() { [[ $EUID -ne 0 ]] && { log_error "需要 root 权限"; exit 1; } } # 在主脚本中引用库 # source "$(dirname "$0")/lib/common.sh" # 或 # . "$(dirname "$0")/lib/common.sh" bash
🏋️ 今日练习
- 创建一个函数库
utils.sh,包含:颜色输出、记录日志到文件、检查命令存在、获取IP地址等函数 - 写一个函数,接收数组名作为参数,统计数组中最大值、最小值和平均值
- 用关联数组实现一个简单的配置文件解析器,读取 key=value 格式的配置文件到关联数组
- 写一个计算器脚本,支持加减乘除和幂运算,参数格式:
calc.sh 10 + 3 - 用递归函数计算斐波那契数列第 N 项(注意 N 不要太大)