定义函数

# 方式 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 的一部分执行——所以定义的函数能用。

下一篇:数组与字符串处理。