Linux Shell 函数:从定义到实战,让脚本更高效

Linux Shell 函数:从定义到实战,让脚本更高效

  • [Linux Shell 函数:从定义到实战,让脚本更高效](#Linux Shell 函数:从定义到实战,让脚本更高效)
    • [一、Shell 函数是什么?为什么要用它?](#一、Shell 函数是什么?为什么要用它?)
    • [二、Shell 函数的基础定义:两种语法](#二、Shell 函数的基础定义:两种语法)
      • [语法 1:标准简洁格式(推荐)](#语法 1:标准简洁格式(推荐))
      • [语法 2:带 `function` 关键字(更直观)](#语法 2:带 function 关键字(更直观))
      • 注意事项(避坑)
    • [三、Shell 函数的调用:像用命令一样简单](#三、Shell 函数的调用:像用命令一样简单)
      • [示例 1:简单函数调用](#示例 1:简单函数调用)
      • [示例 2:多次调用函数](#示例 2:多次调用函数)
    • [四、Shell 函数的传参:给函数"喂数据"](#四、Shell 函数的传参:给函数“喂数据”)
      • [核心规则:用 `n\` 接收参数](#核心规则:用 `n` 接收参数)
      • [示例 1:传递单个参数](#示例 1:传递单个参数)
      • [示例 2:传递多个参数](#示例 2:传递多个参数)
      • [示例 3:接收所有参数(`@\`)](#示例 3:接收所有参数(`@`))
    • [五、Shell 函数的返回值:两种"返回"方式](#五、Shell 函数的返回值:两种“返回”方式)
      • [1. 返回状态码(用 `return` 语句)](#1. 返回状态码(用 return 语句))
        • [示例:用 `return` 判断文件是否存在](#示例:用 return 判断文件是否存在)
      • [2. 返回数据结果(用 `echo` 输出)](#2. 返回数据结果(用 echo 输出))
        • [示例:用 `echo` 返回计算结果](#示例:用 echo 返回计算结果)
    • [六、Shell 函数的变量作用域:局部变量 vs 全局变量](#六、Shell 函数的变量作用域:局部变量 vs 全局变量)
    • 七、实战案例:用函数编写一个"系统检查脚本"
    • 八、总结

Linux Shell 函数:从定义到实战,让脚本更高效

在 Linux 日常运维或自动化脚本编写中,你是否经常遇到"重复编写相同命令"的场景?比如多次检查文件是否存在、重复格式化输出日志、频繁执行相同的计算逻辑。如果每次都复制粘贴代码,不仅脚本臃肿难维护,还容易因修改遗漏导致 bug。

Shell 函数 正是解决这一问题的"利器"------它能将一系列命令封装成一个可复用的"代码块",让脚本更简洁、更易读、更易维护。今天我们就从基础到实战,全面掌握 Shell 函数的定义、调用、传参等核心用法。

一、Shell 函数是什么?为什么要用它?

在开始学习前,我们先明确一个概念:Shell 函数 是 Shell 脚本中一组命名的命令集合,本质是"代码复用单元"。它的核心价值在于:

  • 减少重复代码:相同逻辑只需定义一次,后续多次调用即可;
  • 提升脚本可读性 :用"函数名"(如 check_filelog_info)替代一堆命令,脚本逻辑更清晰;
  • 便于维护:若需修改逻辑,只需改函数内部,无需逐个改调用处;
  • 模块化编程:将复杂脚本拆分为多个函数(如"数据读取""计算""输出"),结构更清晰。

二、Shell 函数的基础定义:两种语法

Shell(以最常用的 bash 为例)定义函数有两种标准语法,本质功能一致,可根据个人习惯选择。

语法 1:标准简洁格式(推荐)

无需关键字,直接用"函数名+括号"开头,函数体用大括号包裹,大括号前后必须有空格(语法要求)。

bash 复制代码
函数名() {
    # 函数体:要执行的命令
    命令1
    命令2
    ...
}

语法 2:带 function 关键字(更直观)

通过 function 关键字明确声明"这是一个函数",括号可省略,可读性更强,适合新手。

bash 复制代码
function 函数名 {
    # 函数体:要执行的命令
    命令1
    命令2
    ...
}

注意事项(避坑)

  1. 函数名规则 :只能包含字母、数字、下划线,且不能以数字开头 (如 123func 错误,func123 正确);
  2. 大括号空格{ 后必须有空格或换行,} 前必须有空格或命令(如 {echo "hi"} 错误,{ echo "hi" } 正确);
  3. 定义位置:函数必须先定义,再调用(若函数在脚本末尾定义,前面调用会报错"命令未找到")。

三、Shell 函数的调用:像用命令一样简单

定义好函数后,调用方式非常直接------直接写函数名,无需加括号(这和其他编程语言不同,需注意)。

示例 1:简单函数调用

定义一个打印"Linux 脚本欢迎语"的函数,并调用它:

bash 复制代码
#!/bin/bash

# 定义函数(标准格式)
welcome() {
    echo "====================="
    echo "  Welcome to Linux Shell"
    echo "====================="
}

# 调用函数(直接写函数名)
welcome

执行脚本(假设脚本名为 func_demo.sh):

bash 复制代码
chmod +x func_demo.sh  # 赋予执行权限
./func_demo.sh

输出结果:

复制代码
=====================
  Welcome to Linux Shell
=====================

示例 2:多次调用函数

若需重复执行某逻辑,只需多次调用函数即可,避免重复写命令:

bash 复制代码
#!/bin/bash

# 定义函数:打印分隔线
print_line() {
    echo "---------------------"
}

# 多次调用
print_line
echo "第一步:检查系统状态"
print_line
echo "第二步:备份数据"
print_line
echo "第三步:执行完成"
print_line

输出结果:

复制代码
---------------------
第一步:检查系统状态
---------------------
第二步:备份数据
---------------------
第三步:执行完成
---------------------

四、Shell 函数的传参:给函数"喂数据"

和 Linux 命令(如 ls -l-l 是参数)一样,Shell 函数也能接收外部传入的参数,实现"动态逻辑"(比如根据不同参数处理不同数据)。

核心规则:用 $n 接收参数

Shell 函数没有"形参列表"(如其他语言的 func(a,b)),而是通过固定变量接收参数:

  • $1:接收第 1 个参数,$2:接收第 2 个参数,以此类推;
  • $0不表示函数的第 0 个参数,而是表示脚本本身的文件名(和函数无关);
  • $#:表示函数接收的参数总个数;
  • $@ / $*:表示所有参数(两者在双引号包裹时略有区别,见《Shell 中 $@$* 的核心区别:双引号包裹下的关键差异解析》,日常用 $@ 更安全)。

示例 1:传递单个参数

定义一个"问候函数",根据传入的名字,打印个性化问候:

bash 复制代码
#!/bin/bash

# 定义函数:接收 1 个参数(名字)
greet() {
    # $1 接收第一个参数
    echo "Hello, $1! Welcome to Shell Function."
}

# 调用函数:传入参数(函数名后直接跟参数,空格分隔)
greet "Alice"
greet "Bob"

输出结果:

复制代码
Hello, Alice! Welcome to Shell Function.
Hello, Bob! Welcome to Shell Function.

示例 2:传递多个参数

定义一个"计算两数之和"的函数,接收两个数字参数,返回结果:

bash 复制代码
#!/bin/bash

# 定义函数:接收 2 个参数(num1, num2)
add() {
    # 检查参数个数是否正确($# 是参数总数)
    if [ $# -ne 2 ]; then
        echo "错误:请传入 2 个数字参数!"
        return 1  # 返回错误状态码(非 0 表示失败)
    fi

    # 计算求和($1 是第一个数,$2 是第二个数)
    local sum=$(( $1 + $2 ))  # local:声明局部变量,仅函数内部可用
    echo "计算结果:$1 + $2 = $sum"
}

# 调用函数:传入两个参数
add 3 5
add 10 20
# 错误调用:少传一个参数
add 7

输出结果:

复制代码
计算结果:3 + 5 = 8
计算结果:10 + 20 = 30
错误:请传入 2 个数字参数!

示例 3:接收所有参数($@

定义一个"批量打印参数"的函数,接收任意个数的参数,逐个输出:

bash 复制代码
#!/bin/bash

# 定义函数:接收任意个数的参数
print_params() {
    echo "你传入的参数总数:$# 个"
    echo "参数列表:"
    # 循环遍历所有参数($@ 表示所有参数)
    for param in "$@"; do
        echo "- $param"
    done
}

# 调用函数:传入 3 个参数
print_params "Linux" "Shell" "Function"
# 调用函数:传入 2 个参数(含空格的参数需用双引号包裹)
print_params "Hello World" "Bash Script"

输出结果:

复制代码
你传入的参数总数:3 个
参数列表:
- Linux
- Shell
- Function
你传入的参数总数:2 个
参数列表:
- Hello World
- Bash Script

五、Shell 函数的返回值:两种"返回"方式

Shell 函数的"返回值"和其他编程语言(如 Python、C)不同,它有两种常见场景:返回"状态码" (判断成功/失败)和 返回"数据结果"(如计算值、字符串)。

1. 返回状态码(用 return 语句)

  • return 是 Shell 函数的关键字,仅用于返回 0-255 的整数(状态码);
  • 约定:return 0 表示函数执行成功,return 非 0(如 1、2)表示执行失败(可自定义错误码);
  • 外部通过 $? 获取函数的返回状态码($? 表示"上一个命令/函数的执行状态")。
示例:用 return 判断文件是否存在
bash 复制代码
#!/bin/bash

# 定义函数:检查文件是否存在(参数:文件路径)
check_file() {
    local file_path=$1
    # 检查参数是否为空
    if [ -z "$file_path" ]; then
        echo "错误:请传入文件路径!"
        return 1  # 错误码 1:参数为空
    fi
    # 检查文件是否存在
    if [ -f "$file_path" ]; then
        echo "文件 $file_path 存在"
        return 0  # 成功码 0:文件存在
    else
        echo "文件 $file_path 不存在"
        return 2  # 错误码 2:文件不存在
    fi
}

# 调用函数:检查 /etc/passwd(系统默认存在的文件)
check_file "/etc/passwd"
echo "函数返回状态码:$?"  # 获取成功状态码 0

echo "---------------------"

# 调用函数:检查不存在的文件
check_file "/tmp/not_exist.txt"
echo "函数返回状态码:$?"  # 获取错误状态码 2

输出结果:

复制代码
文件 /etc/passwd 存在
函数返回状态码:0
---------------------
文件 /tmp/not_exist.txt 不存在
函数返回状态码:2

2. 返回数据结果(用 echo 输出)

如果需要函数返回"非整数数据"(如字符串、计算结果),return 无法满足(只能返回 0-255 整数),此时需用 echo 输出数据 ,外部通过"命令替换"($(函数名))捕获结果。

示例:用 echo 返回计算结果
bash 复制代码
#!/bin/bash

# 定义函数:计算两数乘积(返回数据结果)
multiply() {
    local num1=$1
    local num2=$2
    local result=$(( num1 * num2 ))
    # 用 echo 输出结果(外部通过 $(multiply) 捕获)
    echo $result
}

# 调用函数:用 $(multiply) 捕获 echo 输出的结果
product=$(multiply 4 6)
echo "4 * 6 = $product"

# 二次使用结果:将返回值作为参数传入另一个计算
double_product=$(multiply $product 2)
echo "乘积的 2 倍:$double_product"

输出结果:

复制代码
4 * 6 = 24
乘积的 2 倍:48

六、Shell 函数的变量作用域:局部变量 vs 全局变量

Shell 函数中的变量默认是 全局变量 (即函数内外都能访问、修改),若需限制变量仅在函数内部可用,需用 local 关键字声明为 局部变量

示例:局部变量与全局变量的区别

bash 复制代码
#!/bin/bash

# 全局变量:函数内外都能访问
global_var="我是全局变量"

# 定义函数
test_var() {
    # 局部变量:仅函数内部可用(外部无法访问)
    local local_var="我是局部变量"
    # 修改全局变量
    global_var="全局变量被函数修改了"

    echo "函数内部:"
    echo "局部变量 local_var:$local_var"
    echo "全局变量 global_var:$global_var"
}

# 调用函数
test_var

echo "---------------------"
echo "函数外部:"
echo "全局变量 global_var:$global_var"  # 能访问修改后的全局变量
echo "局部变量 local_var:$local_var"    # 无法访问(输出空)

输出结果:

复制代码
函数内部:
局部变量 local_var:我是局部变量
全局变量 global_var:全局变量被函数修改了
---------------------
函数外部:
全局变量 global_var:全局变量被函数修改了
局部变量 local_var:

建议:优先用局部变量

在函数内部定义变量时,尽量用 local 声明为局部变量,避免:

  • 函数内部变量意外修改全局变量,导致脚本逻辑混乱;
  • 变量名冲突(不同函数用相同变量名,互不干扰)。

七、实战案例:用函数编写一个"系统检查脚本"

学到这里,我们已经掌握了函数的核心用法,现在通过一个实战案例,将函数整合到实际脚本中------编写一个"Linux 系统基础检查脚本",包含"检查内存""检查磁盘""检查 CPU"三个功能模块。

bash 复制代码
#!/bin/bash
# 系统基础检查脚本:用函数拆分功能

# 1. 函数:检查内存使用情况
check_memory() {
    echo "================ 内存使用情况 ================"
    # 用 free 命令查看内存,-h 表示人性化单位(GB/MB)
    free -h | grep "Mem:"
    echo ""  # 空行分隔,提升可读性
}

# 2. 函数:检查磁盘使用情况(指定 / 分区)
check_disk() {
    local mount_point=$1
    echo "================ 磁盘使用情况($mount_point) ================"
    # 用 df 命令查看磁盘,-h 人性化单位,--output 只显示需要的列
    df -h --output=source,fstype,size,used,avail,pcent,target | grep "$mount_point"
    echo ""
}

# 3. 函数:检查 CPU 核心数和负载
check_cpu() {
    echo "================ CPU 信息 ================"
    # 查看 CPU 核心数(grep -c 统计行数)
    echo "CPU 核心数:$(grep -c "processor" /proc/cpuinfo)"
    # 查看 CPU 负载(top -b -n1 非交互式执行一次 top 命令)
    echo "CPU 1分钟负载:$(top -b -n1 | grep "Cpu(s)" | awk '{print $2}')"
    echo ""
}

# 主逻辑:调用上述函数
echo "========== Linux 系统基础检查报告 =========="
check_memory
check_disk "/"  # 检查根分区
check_disk "/home"  # 检查 home 分区(若存在)
check_cpu
echo "========== 检查完成 =========="

执行脚本后,输出结果类似:

复制代码
========== Linux 系统基础检查报告 ==========
================ 内存使用情况 ================
Mem:           7.7Gi       1.2Gi       5.8Gi       132Mi       682Mi       6.2Gi

================ 磁盘使用情况(/) ================
/dev/sda1      ext4        40Gi        12Gi        26Gi       32% /

================ 磁盘使用情况(/home) ================
/dev/sda3      ext4       100Gi        25Gi        70Gi       27% /home

================ CPU 信息 ================
CPU 核心数:4
CPU 1分钟负载:0.5

========== 检查完成 ==========

这个脚本通过函数拆分功能,每个函数负责一个具体任务,后续若需新增"检查网络"功能,只需新增一个 check_network() 函数并调用,脚本扩展性极强。

八、总结

Shell 函数是 Linux 脚本编写的核心工具,掌握它能让你的脚本从"杂乱的命令堆"升级为"模块化的高效工具"。核心要点回顾:

  1. 定义 :两种语法(func() {}function func {}),注意大括号空格;
  2. 调用:直接写函数名,无需括号;
  3. 传参 :用 $1$2 接收参数,$# 统计个数,$@ 获取所有参数;
  4. 返回值return 返回状态码(0 成功),echo 返回数据结果(外部用 $(func) 捕获);
  5. 变量作用域local 声明局部变量,避免全局污染;
  6. 实战:用函数拆分脚本功能,提升可读性和可维护性。

下次编写 Shell 脚本时,不妨先思考:"这段逻辑是否会重复使用?是否可以封装成函数?"------养成用函数的习惯,你的脚本能力会快速提升!

若有转载,请标明出处:https://blog.csdn.net/CharlesYuangc/article/details/153042774

相关推荐
璞致电子5 小时前
fpga开发板ZYNQ 璞致 PZ7010/7020 邮票孔核心板简介-ZYNQ7000系列小系统学习板
linux·嵌入式硬件·学习·fpga开发·fpga·fpga开发板·xilinx开发板
第四维度45 小时前
【全志V821_FoxPi】9-2 Linux IIC驱动MPU6050
linux·传感器·tina·mpu6050·v821
isyangli_blog6 小时前
(6)数据中心、台式(塔式)服务器、机架式服务器、刀片式服务器
运维·服务器
tq026 小时前
Cookie和Seeion在客户端和服务端的角色作用
运维·服务器·安全
mjx65306 小时前
PyTorch快速入门
linux
Miki Makimura6 小时前
Reactor 模式实现:从 epoll 到高并发调试
运维·服务器·c++·学习
00后程序员张7 小时前
【Python】基于 PyQt6 和 Conda 的 PyInstaller 打包工具
运维·服务器·数据库
DeeplyMind7 小时前
AMD KFD的BO设计分析系列6-1: VRAM BO的显存分配分析
linux·驱动开发·amdgpu·rocm·kfd
❀͜͡傀儡师8 小时前
使用docker 安装dragonfly带配置文件(x86和arm)版本
运维·docker·容器