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
判断文件是否存在)
- [示例:用 `return` 判断文件是否存在](#示例:用
- [2. 返回数据结果(用 `echo` 输出)](#2. 返回数据结果(用
echo
输出)) -
- [示例:用 `echo` 返回计算结果](#示例:用
echo
返回计算结果)
- [示例:用 `echo` 返回计算结果](#示例:用
- [1. 返回状态码(用 `return` 语句)](#1. 返回状态码(用
- [六、Shell 函数的变量作用域:局部变量 vs 全局变量](#六、Shell 函数的变量作用域:局部变量 vs 全局变量)
- 七、实战案例:用函数编写一个"系统检查脚本"
- 八、总结
Linux Shell 函数:从定义到实战,让脚本更高效
在 Linux 日常运维或自动化脚本编写中,你是否经常遇到"重复编写相同命令"的场景?比如多次检查文件是否存在、重复格式化输出日志、频繁执行相同的计算逻辑。如果每次都复制粘贴代码,不仅脚本臃肿难维护,还容易因修改遗漏导致 bug。
而 Shell 函数 正是解决这一问题的"利器"------它能将一系列命令封装成一个可复用的"代码块",让脚本更简洁、更易读、更易维护。今天我们就从基础到实战,全面掌握 Shell 函数的定义、调用、传参等核心用法。
一、Shell 函数是什么?为什么要用它?
在开始学习前,我们先明确一个概念:Shell 函数 是 Shell 脚本中一组命名的命令集合,本质是"代码复用单元"。它的核心价值在于:
- 减少重复代码:相同逻辑只需定义一次,后续多次调用即可;
- 提升脚本可读性 :用"函数名"(如
check_file
、log_info
)替代一堆命令,脚本逻辑更清晰; - 便于维护:若需修改逻辑,只需改函数内部,无需逐个改调用处;
- 模块化编程:将复杂脚本拆分为多个函数(如"数据读取""计算""输出"),结构更清晰。
二、Shell 函数的基础定义:两种语法
Shell(以最常用的 bash
为例)定义函数有两种标准语法,本质功能一致,可根据个人习惯选择。
语法 1:标准简洁格式(推荐)
无需关键字,直接用"函数名+括号"开头,函数体用大括号包裹,大括号前后必须有空格(语法要求)。
bash
函数名() {
# 函数体:要执行的命令
命令1
命令2
...
}
语法 2:带 function
关键字(更直观)
通过 function
关键字明确声明"这是一个函数",括号可省略,可读性更强,适合新手。
bash
function 函数名 {
# 函数体:要执行的命令
命令1
命令2
...
}
注意事项(避坑)
- 函数名规则 :只能包含字母、数字、下划线,且不能以数字开头 (如
123func
错误,func123
正确); - 大括号空格 :
{
后必须有空格或换行,}
前必须有空格或命令(如{echo "hi"}
错误,{ echo "hi" }
正确); - 定义位置:函数必须先定义,再调用(若函数在脚本末尾定义,前面调用会报错"命令未找到")。
三、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 脚本编写的核心工具,掌握它能让你的脚本从"杂乱的命令堆"升级为"模块化的高效工具"。核心要点回顾:
- 定义 :两种语法(
func() {}
或function func {}
),注意大括号空格; - 调用:直接写函数名,无需括号;
- 传参 :用
$1
、$2
接收参数,$#
统计个数,$@
获取所有参数; - 返回值 :
return
返回状态码(0 成功),echo
返回数据结果(外部用$(func)
捕获); - 变量作用域 :
local
声明局部变量,避免全局污染; - 实战:用函数拆分脚本功能,提升可读性和可维护性。
下次编写 Shell 脚本时,不妨先思考:"这段逻辑是否会重复使用?是否可以封装成函数?"------养成用函数的习惯,你的脚本能力会快速提升!
若有转载,请标明出处:https://blog.csdn.net/CharlesYuangc/article/details/153042774