Shell 脚本函数与参数传递:函数定义/返回值/作用域

小飞兽 Shell 106 次阅读 2026-07-29

一、引言

随着脚本规模增大,重复代码会成为维护噩梦。函数将可复用的逻辑封装为独立单元,是编写可维护 Shell 脚本的关键技能。

二、函数定义与调用

#!/bin/bash

函数定义与调用示例

方式一:使用 function 关键字

function say_hello { echo "你好,欢迎学习 Shell 函数!" }

方式二:直接使用函数名(bash 原生写法)

greet() { echo "Hello, World!" }

函数调用

say_hello greet

带参数的函数调用

function print_info { echo "姓名:$1" echo "年龄:$2" } print_info "张三" 25

三、参数传递与特殊变量

#!/bin/bash

参数传递与特殊变量示例

function demo_params {
echo "函数名称:$0"
echo "第一个参数:$1"
echo "第二个参数:$2"
echo "所有参数(字符串):$*"
echo "所有参数(数组):$@"
echo "参数个数:$#"

echo "=== 使用 $@ 遍历参数 ==="
for param in "$@"; do
echo "参数:$param"
done

echo "=== shift 操作 ==="
echo "原始 $1=$1, $2=$2"
shift
echo "shift 后 $1=$1"
}

demo_params "苹果" "香蕉" "橘子" "葡萄"

四、返回值处理

#!/bin/bash

返回值处理示例

方法一:通过 return 返回退出状态码(0-255)

function is_even { if [ $(($1 % 2)) -eq 0 ]; then return 0 else return 1 fi }

num=10
if is_even $num; then
echo "$num 是偶数"
else
echo "$num 是奇数"
fi

方法二:通过 echo 返回数据

function get_sum { local a=$1 local b=$2 echo $((a + b)) } result=$(get_sum 100 200) echo "100 + 200 = $result"

方法三:通过全局变量返回数据

declare -g g_result function get_product { g_result=$(($1 * $2)) } get_product 15 8 echo "15 * 8 = $g_result"

方法四:使用 nameref(bash 4+)传递变量引用

function double_value { local -n ref=$1 ref=$((ref * 2)) } num=50 double_value num echo "num 翻倍后 = $num"

五、变量作用域

#!/bin/bash

变量作用域示例

全局变量

global_var="我是全局变量"

function test_scope {
# local 关键字声明局部变量
local local_var="我是局部变量"
echo "局部变量:$local_var"

# 在函数内修改全局变量
global_var="函数内修改后的值"
}

test_scope
echo "函数外全局变量:$global_var"

echo "=== 全局与局部变量同名时的处理 ==="
variable="全局"

function test_shadow {
local variable="局部"
echo "函数内:$variable"
}
test_shadow
echo "函数外:$variable"

六、函数库与脚本复用

#!/bin/bash

函数库示例

---- lib_logger.sh ----

readonly LOG_LEVEL_DEBUG=0 readonly LOG_LEVEL_INFO=1 readonly LOG_LEVEL_WARN=2 readonly LOG_LEVEL_ERROR=3

LOG_LEVEL=$LOG_LEVEL_INFO

log_debug() { [ $LOG_LEVEL -le $LOG_LEVEL_DEBUG ] && echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') $*" >&2; }
log_info() { [ $LOG_LEVEL -le $LOG_LEVEL_INFO ] && echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') $*" >&2; }
log_warn() { [ $LOG_LEVEL -le $LOG_LEVEL_WARN ] && echo "[WARN] $(date '+%Y-%m-%d %H:%M:%S') $*" >&2; }
log_error() { echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') $*" >&2; }

---- lib_util.sh ----

command_exists() { command -v "$1" &> /dev/null; } service_running() { systemctl is-active --quiet "$1"; }

---- 使用函数库 ----

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/lib_logger.sh" source "$SCRIPT_DIR/lib_util.sh"

LOG_LEVEL=$LOG_LEVEL_DEBUG
log_info "脚本开始执行"

if command_exists docker; then
log_info "Docker 已安装"
else
log_error "Docker 未安装"
fi

七、常见问题(FAQ)

Q1: 函数内的变量会覆盖函数外的同名变量吗?

使用 local 声明的局部变量不会影响全局变量。没有 local 关键字时,函数内的赋值会直接修改全局变量。

Q2: 如何从函数返回一个大于 255 的整数?

return 语句只能返回 0-255 的退出状态码。如果需要返回大整数或字符串,应该用 echo 输出,然后通过 $(func) 捕获。

Q3: bash 函数支持默认参数值吗?

可以手动实现:param=${1:-default_value}。如果调用时未传该参数,可以用 ${1:-default} 语法提供默认值。

Q4: 函数支持递归调用吗?

bash 支持递归函数,但默认递归深度有限。递归函数示例:factorial() { [ $1 -le 1 ] && echo 1 || echo $(( $1 * $(factorial $(($1-1))) )); }

八、延伸阅读