跳到主要内容

Linux Shell 函数教程:定义、参数、local 与返回值

函数用来把一段可复用的逻辑封装起来。学会函数后,脚本会更容易读、更容易测,也更不容易把主流程写成长长的一坨。

本章以 Bash 为例。如果你的脚本要以 /bin/sh(在 Ubuntu 上常为 dash)运行,请注意部分写法的可移植性差异。

函数定义与调用

推荐使用不带 function 关键字的可移植写法:

#!/bin/bash

say_hello()
{
echo "Hello, $1"
}

say_hello "GetIoT"

要点:

  • 函数必须先定义,后调用
  • 定义时圆括号里不写形参列表;调用时像普通命令一样传参。
  • 函数体内用 $1$2$#$@ 等接收参数,含义与脚本位置参数类似,但作用域是当前这次函数调用
  • 函数内的 $0 通常仍是脚本名,不是函数名(Bash 行为如此)。

也可以写成单行:

say_hello() { echo "Hello, $1"; }

多语句时仍建议使用花括号分块,并保持缩进一致。

函数规范

函数结构

  • 每一个小功能都封装到独立函数里,一个函数只完成一件事;在脚本尾部按顺序调用函数,把整段逻辑串起来。
  • 控制函数体大小,尽量控制在大约一百行以内;过大的函数应拆成多个。

函数复用

  • 多次出现的相同逻辑要抽成函数,降低维护成本。
  • 对常用能力(写日志、计算日期、发邮件等)可封装成独立脚本,再由其他脚本 source 引入。

局部变量

默认情况下,函数里赋值的变量是全局的,会污染脚本其余部分。需要局部作用域时,用 local

#!/bin/bash

counter=0

inc()
{
local step=$1
counter=$((counter + step))
echo "inside: counter=$counter, step=$step"
}

inc 3
echo "outside: counter=$counter"
# echo "$step" # 空:step 是 local,函数外不可见
提示

配置类、脚本级变量可以放在脚本头部;函数内部的临时变量优先使用 local

函数返回

Shell 函数的“返回”通常有两种用途:

  1. 告诉调用方成功或失败(退出状态)。
  2. 文本结果交回给调用方。

用 return 返回状态码

return 返回的是整数状态码(一般为 0–255),约定与命令退出码相同:0 表示成功,非 0 表示失败。

#!/bin/bash

is_positive()
{
if [ "$1" -gt 0 ] 2>/dev/null; then
return 0
else
return 1
fi
}

if is_positive 10; then
echo "ok"
else
echo "not positive"
fi

在函数外检查上一次状态码,可以用 $?

is_positive -1
echo $? # 1
备注

return 只能用在函数里。在脚本顶层请用 exit。不要指望用 return 传回字符串。

用 echo / printf 返回文本

需要把计算结果交给调用方时,通常把结果打印到标准输出,再用命令替换捕获:

#!/bin/bash

add()
{
local a=$1
local b=$2
echo $((a + b))
}

sum=$(add 3 5)
echo "sum=$sum"

如果函数既要打印调试信息,又要返回结果,调试信息应写到 stderr,避免污染捕获结果:

add()
{
local a=$1
local b=$2
echo "debug: a=$a b=$b" >&2
echo $((a + b))
}

综合示例

下面这个小脚本演示参数检查、局部变量、状态码与文本返回:

greet.sh
#!/bin/bash

usage()
{
echo "Usage: $0 <name>" >&2
}

greet()
{
local name=$1
if [ -z "$name" ]; then
return 1
fi
echo "Hello, ${name}!"
}

if [ $# -lt 1 ]; then
usage
exit 1
fi

msg=$(greet "$1") || {
echo "invalid name" >&2
exit 1
}

echo "$msg"

运行:

$ chmod +x greet.sh
$ ./greet.sh GetIoT
Hello, GetIoT!

常见错误

现象常见原因
command not found调用写在定义之前
变量“莫名其妙”被改掉函数内未使用 local
$(func) 结果里多出调试文字调试信息打到了 stdout,应改用 stderr
return 后拿到奇怪数字把字符串或大于 255 的值当状态码使用了

下一章会介绍 Shell 通配符,它和后面的正则表达式容易混淆,建议对照阅读。