定义函数
# 方式 1(推荐)
greet() {
echo "Hello, $1"
}
# 方式 2
function greet() {
echo "Hello, $1"
}
# 调用
greet "Alice"
⚠ 函数定义必须在调用之前——shell 是顺序解析的。
参数
sum() {
echo $(($1 + $2))
}
result=$(sum 3 5)
echo $result # 8
函数内的特殊变量和脚本里一样:
demo() {
echo "函数名: $FUNCNAME"
echo "第 1 个参数: $1"
echo "参数个数: $#"
echo "所有参数: $@"
}
demo a b c
返回值的两种方式
方式 A:echo + $()
get_user() {
echo "$(whoami)"
}
user=$(get_user)
echo "用户: $user"
适合:返回字符串 / 数字——这是 shell 最常用的方式。
方式 B:return(仅整数 0-255)
is_root() {
[[ "$(id -u)" -eq 0 ]] && return 0 || return 1
}
if is_root; then
echo "是 root"
fi
⚠ return 只能返回 0-255 整数,且语义是退出码(0 成功,非 0 失败)。不能用来传值。
局部变量
process() {
local item="$1" # 仅函数内可见
local count=0
# ...
}
函数内永远用
local声明变量——否则会污染全局。
默认参数
greet() {
local name="${1:-World}" # 没传就用 World
echo "Hello, $name"
}
greet # → Hello, World
greet Alice # → Hello, Alice
实战 1:日志函数封装
#!/bin/bash
log() {
local level="$1"
shift # 把 $1 弹出,$@ 变成剩余参数
local color
case "$level" in
INFO) color="\033[32m" ;; # 绿
WARN) color="\033[33m" ;; # 黄
ERROR) color="\033[31m" ;; # 红
*) color="\033[0m" ;;
esac
printf "%b[%s] %-5s: %s%b\n" "$color" "$(date +%H:%M:%S)" "$level" "$*" "\033[0m"
}
log INFO "服务启动"
log WARN "配置文件用了默认值"
log ERROR "数据库连接失败"
输出(带颜色):
[10:23:45] INFO : 服务启动
[10:23:45] WARN : 配置文件用了默认值
[10:23:45] ERROR: 数据库连接失败
实战 2:错误处理框架
#!/bin/bash
set -euo pipefail
die() {
echo "[FATAL] $*" >&2
exit 1
}
require_cmd() {
command -v "$1" >/dev/null || die "缺少命令: $1"
}
require_file() {
[[ -f "$1" ]] || die "缺少文件: $1"
}
# 主流程
require_cmd jq
require_cmd curl
require_file /etc/myapp/config.ini
# ... 后面代码可以放心用 jq / curl / 配置文件
函数文件(库)
把通用函数放到一个文件,多个脚本 source 进来:
# /opt/lib/common.sh
log() { printf "[%s] %s\n" "$(date +%H:%M:%S)" "$*"; }
die() { echo "$*" >&2; exit 1; }
# script.sh
#!/bin/bash
source /opt/lib/common.sh
log "开始"
[[ -d /opt/data ]] || die "/opt/data 不存在"
source(或简写 .)= 把文件内容当成当前 shell 的一部分执行——所以定义的函数能用。
下一篇:数组与字符串处理。