Shell函数进阶实战:从基础到实战(4)

一:为什么函数是 Shell 脚本的灵魂?

在日常运维工作中,我们经常需要编写大量重复性任务的脚本。如果你的脚本通篇都是“裸奔”的命令,没有函数封装,那只能说明你还在“脚本小白”阶段。真正专业的运维脚本,一定是模块化、可复用、易维护的——而这一切,都离不开函数

本文将系统讲解 Shell 函数的定义、传参、返回值、变量作用域、递归调用、函数库构建等核心知识,并结合实际案例(如动态菱形输出、服务控制封装)带你写出专业级 Shell 脚本


二、Shell 函数基础语法

1. 函数定义方式(两种等效)

bash

编辑

# 方式一:带 function 关键字
function myfunc {
    echo "Hello from function"
}

# 方式二:函数名后加括号(更常用)
myfunc() {
    echo "Hello from function"
}

建议:推荐使用第二种,简洁且兼容性更好。


三、函数传参与返回值

1. 函数传参(位置参数)

函数内部通过 $1, $2... 获取传入参数:

bash

编辑

sum() {
    echo $(($1 + $2))
}

sum 10 20  # 输出 30

2. 函数“返回值”的两种理解

(1)使用 return:仅支持 0~255 的退出状态码

bash

编辑

db1() {
    read -p "请输入数字: " value
    return $((value * 2 % 256))  # 超出 255 会取模
}
db1
echo $?  # 获取 return 值

⚠️ 注意:return 不是返回计算结果,而是退出状态码,类似命令的 exit code。

(2)使用 echo + 命令替换:真正返回“数据”

bash

编辑

db2() {
    read -p "请输入数字: " value
    echo $((value * 2))
}

result=$(db2)
echo "结果是: $result"

最佳实践需要返回计算结果时,用 echo + $();需要表示成功/失败状态时,用 return


四、变量作用域:全局 vs 局部

Shell 中变量默认是全局的,容易造成污染。使用 local 限定函数内变量:

bash

编辑

myfun() {
    local i=8
    echo "函数内 i = $i"
}
i=9
myfun
echo "函数外 i = $i"  # 仍为 9

规范建议:所有函数内部临时变量都应加 local,避免副作用。


五、递归:函数调用自身

案例1:阶乘计算

bash

编辑

fact() {
    if [ $1 -le 1 ]; then
        echo 1
    else
        local n=$(( $1 - 1 ))
        local res=$(fact $n)
        echo $(( $1 * res ))
    fi
}

read -p "输入 n: " n
echo "$n! = $(fact $n)"

案例2:递归遍历目录

bash

编辑

list_files() {
    local dir="$1"
    local indent="$2"
    for f in "$dir"/*; do
        [ -e "$f" ] || continue  # 防止空目录报错
        basename=$(basename "$f")
        if [ -d "$f" ]; then
            echo "${indent}📁 $basename"
            list_files "$f" "  $indent"
        else
            echo "${indent}📄 $basename"
        fi
    done
}

list_files "/var/log" ""

 递归虽强大,但需注意栈深度限制,避免无限递归。


六、实战:动态输出菱形(函数+参数控制)

bash

编辑

#!/bin/bash

draw_diamond() {
    local n=$1
    if ! [[ "$n" =~ ^[0-9]+$ ]] || [ "$n" -le 0 ]; then
        echo "请输入正整数!"
        return 1
    fi

    # 上半部分(含中间行)
    for ((i=1; i<=n; i++)); do
        spaces=$((n - i))
        stars=$((2*i - 1))
        printf "%*s" $((spaces + stars))
        printf "%0.s*" $(seq 1 $stars)
        echo
    done

    # 下半部分
    for ((i=n-1; i>=1; i--)); do
        spaces=$((n - i))
        stars=$((2*i - 1))
        printf "%*s" $((spaces + stars))
        printf "%0.s*" $(seq 1 $stars)
        echo
    done
}

read -p "请输入菱形大小(正整数): " size
draw_diamond "$size"

 效果:输入 3 输出:

text

编辑

  *
 ***
*****
 ***
  *

七、函数库:模块化脚本开发

将通用函数抽离为库文件,实现复用。

1. 创建函数库 myfuncs.sh

bash

编辑

# myfuncs.sh
jiafa() { echo $(($1 + $2)); }
chengfa() { echo $(($1 * $2)); }
chufa() {
    if [ $2 -ne 0 ]; then
        echo $(($1 / $2))
    else
        echo "错误:除数不能为0" >&2
        return 1
    fi
}

2. 在脚本中引用库

bash

编辑

# test.sh
#!/bin/bash
. ./myfuncs.sh  # 或 source ./myfuncs.sh

a=10; b=3
echo "加法: $(jiafa $a $b)"
echo "乘法: $(chengfa $a $b)"
echo "除法: $(chufa $a $b)"

工程化思维:把 myfuncs.sh 放入 /usr/local/lib/,所有脚本均可复用。


八、高级实战:封装服务控制函数(兼容 CentOS 6/7)

bash

编辑

# function.sh
servicectl_usage() {
    echo "Usage: servicectl <service> <start|stop|restart|status>"
    return 1
}

chk_centos_ver() {
    if grep -q "CentOS.*release 7" /etc/centos-release 2>/dev/null; then
        echo "7"
    elif grep -q "CentOS.*release 6" /etc/centos-release 2>/dev/null; then
        echo "6"
    else
        echo "unknown"
    fi
}

servicectl() {
    local service=$1
    local action=$2

    if [ -z "$service" ] || [ -z "$action" ]; then
        servicectl_usage
        return 1
    fi

    case $(chk_centos_ver) in
        7) systemctl "$action" "${service}.service" ;;
        6) service "$service" "$action" ;;
        *) echo "不支持的系统版本"; return 2 ;;
    esac
}

# 调用
servicectl "$1" "$2"

使用方式:

bash

编辑

./function.sh nginx start

此函数可无缝兼容新旧系统,是运维自动化的典型封装。


九、总结:写出专业 Shell 脚本的 5 个准则

  1. 函数化:避免重复代码,每个功能封装成函数。
  2. 参数化:通过参数控制行为,提升灵活性。
  3. 局部变量:函数内变量一律 local
  4. 错误处理:检查参数合法性,合理使用 return 状态码。
  5. 模块化:通用函数抽成库,实现跨脚本复用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值