Linux Shell 编程:函数与数组详解
Linux Shell编程之函数与数组
在Linux Shell编程中,函数和数组是提升脚本模块化、复用性和数据处理能力的核心要素。函数能将重复执行的代码块封装为独立单元,让脚本结构更清晰;数组则为批量存储、处理一组相关数据提供了便捷的方式。掌握二者的使用,是编写高效、可维护Shell脚本的基础,本文将简易讲解Shell函数与数组的核心用法,并补充实用拓展知识点。
一、Shell函数
Shell函数本质是一组Shell命令的集合,被调用时直接从内存执行,相比重复编写命令块,执行效率更高,同时能大幅减少代码冗余,便于脚本的修改和维护。
1. 函数的基本定义与调用
Shell函数定义有简洁式和完整式两种,核心语法一致,可根据习惯选择,function关键字可省略,函数体必须用{}包裹,且命令序列末尾建议加;或换行分隔。
定义语法
# 方式1:省略function(推荐,更简洁)
函数名() {
命令序列
[return 退出值]
}
# 方式2:带function关键字
function 函数名() {
命令序列
[return 退出值]
}
-
return:用于退出函数并返回一个0-255的整数退出值,0表示执行成功,非0表示失败;若省略,函数将以最后一条命令的执行结果作为返回值。也可以使用exit终止整个shell脚本。 -
与
exit的区别:return仅退出函数,不影响脚本整体执行;exit会直接终止整个Shell脚本 -
"{"符号表示函数执行命令的入口,该符号可以与函数名同行也可以在函数名下一行的句首;
-
"}"符号表示函数体结束,两个大括号之间{ }是函数体;
-
"function"关键字表示定义一个函数,可以省略;
-
"命令序列"部分可以是任意的shell命令,也可以调用其他函数;
调用语法
直接通过函数名+参数(可选) 调用,无需加括号,参数之间用空格分隔:
函数名 [参数1] [参数2] ...
示例:两数求和
vim sum.sh
#!/bin/bash
# 定义sum函数
sum() {
#提示用户输入第一个数
echo "请输入第一个数:"
read num1
#检查输入是否为有效的数字
if ! [[ $num1 =~ ^[0-9+$ ]]; then
echo “输入的第一个数不是有效的整数,请重新运行脚本并输入正确的数字。”
return 1
fi
#提示用户输入第二个数
echo “请输入第二个数:”
read num2
#检查输入是否为有效的数字
if ! [[ $num2 =~ ^[0-9+$ ]]; then
echo “输入的第一个数不是有效的整数,请重新运行脚本并输入正确的数字。”
return 1
fi
#计算两数之和
result=$((num1 + num2))
#输出结果
echo "两数之和为:$result"
}
#调用sum函数
sum
运行脚本:bash sum.sh
示例2:系统资源监控并报警函数
该函数会定期监控系统的cpu和内存使用率,当使用率超过设定的阈值时,会发送报警信息,这里简单模拟为输出到控制台,实际应用中可扩展为发送邮件,短信等。
vim jiankong.sh
#!/bin/bash
#发送报警信息的函数
send_alert() {
local message=$1
echo "ALERT: $message"
#这里可以添加发送邮件,短信等报警逻辑
}
#系统资源1监控函数
monitor_system_rescources() {
local cpu_thershold=$1
local mem_thershold=$2
local interval=$3
while true; do
#获取cpu使用率
cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2 + $4}')
# 获取内存使用率
mem_usage=$(free | awk '/Mem/ {printf("%.2f, $3/$2 * 100)}')
if (( $(echo "$cpu_usage > $cpu_threshold" | bc -1) )); then
send_alert "CPU usage ($cpu_usage%)."
fi
if (( $(echo "$mem_usage > $mem_threshold" | bc -1) )); then
send_alert "Memory usage ($mem_usage%)."
fi
sleep "$interval"
done
}
#调用函数
monitor_system_rescources 8 1 5
参数说明:函数接收三个参数,分别是CPU使用率阈值,内存使用率已经监控间隔时间(秒)。
循环监控:使用while true循环不断监控系统资源。通过top和free命令获取cpu和内存使用率,使用bc命令进行浮点数比较。
报警处理:当使用率超过阈值时,调用send_alert函数发送报警信息。
2. 函数变量的作用域
Shell函数默认在当前脚本的全局环境中执行,函数内未特殊声明的变量为全局变量,脚本内任何位置都可访问;若需将变量限定在函数内部,避免与全局变量重名冲突,需用local关键字定义局部变量。
核心规则
-
局部变量:
local 变量名=值,仅在函数内部有效,函数执行结束后自动销毁。 -
全局变量:直接定义
变量名=值,脚本内所有位置(包括函数内)均可访问和修改。
示例
vim fun_scope.sh
#!/bin/bash
# 全局变量
a=10
# 定义函数
test_var() {
local a=20 # 局部变量
echo "函数内的a:$a" # 输出20
}
# 调用函数
test_var
echo "函数外的a:$a" # 输出10,全局变量未被修改
执行脚本:bash fun_scope.sh
3. 函数的参数传递
Shell函数不支持在定义时声明参数,而是在调用时通过位置参数传递,函数内部通过$n(n为数字)获取参数,规则与脚本的位置参数一致:
-
$1、$2...$9:表示第1到第9个参数; -
${10}、${11}...:第10个及以后的参数,必须用大括号包裹,否则无法识别; -
辅助参数:
$#获取参数个数,$@/$*获取所有参数。
示例:计算多个数的和
#!/bin/bash
# 定义求和函数,接收任意多个数字参数
sum_many() {
local total=0
# 遍历所有参数
for num in $@
do
total=$((total + num))
done
echo "所有参数的和为:$total"
}
# 调用函数,传递5个参数
sum_many 1 2 3 4 5
4. 递归函数
递归函数是指在函数内部调用自身的函数,需设置递归终止条件,否则会陷入无限递归。Shell递归函数常用于遍历目录、计算阶乘、斐波那契数列等场景。
示例1:计算n的阶乘
#!/bin/bash
# 阶乘递归函数,n! = n*(n-1)!,终止条件:1! = 1
factorial() {
local n=$1
if [ $n -eq 1 ]; then
echo 1
else
# 递归调用,计算(n-1)的阶乘
local pre=$(factorial $((n-1)))
echo $((n * pre))
fi
} # 调用函数,计算5的阶乘
factorial 5
示例2:递归遍历目录(补充:显示层级)
在原文档基础上补充层级缩进,让遍历结果更清晰:
#!/bin/bash
# 递归遍历目录,$1为目录路径,$2为层级缩进符
traverse_dir() {
local dir=$1
local indent=$2
# 遍历目录下所有内容
for item in $dir/*
do
if [ -d $item ]; then
echo "${indent}├── 目录:$(basename $item)"
# 递归调用,缩进符加两个空格
traverse_dir $item "${indent} "
elif [ -f $item ]; then
echo "${indent}├── 文件:$(basename $item)"
fi
done
}
# 调用函数,从当前目录开始,初始缩进为空
traverse_dir "." ""
5. 补充知识点:函数的返回值进阶
原文档仅提及return返回值,实际使用中,return仅能返回0-255的整数,若需返回字符串、大数值等,可通过标准输出(echo) 结合变量接收实现,这是Shell函数返回复杂结果的常用技巧。
示例:返回字符串结果
#!/bin/bash
# 定义函数,返回系统简单信息
get_sys_info() {
local hostname=$(hostname)
local kernel=$(uname -r)
# 通过echo输出结果,作为函数的"返回值"
echo "主机名:$hostname,内核版本:$kernel"
}
# 用变量接收函数的标准输出
sys_info=$(get_sys_info)
echo "系统信息:$sys_info"
二、Shell数组
Shell仅支持一维数组,无需提前定义数组长度,元素下标从0开始,可存储整数、字符串等任意类型数据,是批量处理数据的核心工具。数组的定义和操作语法简洁,支持遍历、切片、替换、删除等常用操作。
1. 数组的4种定义方法
Shell数组用()表示,元素之间用空格分隔,以下4种定义方法可根据场景灵活选择,其中方法1是最常用的简洁式。
方法1:直接定义(推荐)
数组名=(值1 值2 值3 ...)
方法2:指定下标定义(可非连续下标)
数组名=([0]=值1 [2]=值2 [5]=值3 ...)
方法3:通过列表字符串定义
list_str="值1 值2 值3" 数组名=($list_str)
方法4:单个下标赋值(适合动态添加元素)
数组名[0]=值1 数组名[1]=值2 数组名[2]=值3
注意:若元素包含空格,需用单/双引号包裹,例如arr=("hello world" test 123)。
2. 数组的操作
Shell数组的核心操作包括获取长度、读取元素、遍历元素,是后续复杂操作的基础,语法固定且易记。
(1)获取数组长度
通过${#数组名[@]}或${#数组名[*]}实现,二者效果完全一致,返回数组的元素个数。
arr_number=(1 2 3 4 5)
arr_length=${#arr_number[*]} 或 arr_length_1=${#arr_number[@]}
echo $arr_length echo $arr_length_1
5
(2)读取指定下标元素
语法:${数组名[下标]},下标从0开始;若下标为@或*,表示读取数组所有元素。
arr_index2=${arr_number[2]} (第3个元素)
echo $arr_index2
3
(3)遍历数组元素
最常用for循环遍历,结合${数组名[@]}获取所有元素,适合对每个元素做批量处理。
vim arrary_traverse.sh
#!/bin/bash
arr_number=(1 2 3 4 5)
for v in ${arr_number[@]}
do
echo $v
done
bash arrary_tranverse.sh (执行脚本)
1
2
3
4
5
3. 数组的高级操作
Shell数组支持切片、替换、删除等高级操作,无需额外命令,通过内置语法即可实现,大幅提升数据处理效率。
(1)数组切片
从数组中提取连续的部分元素,形成新的元素集合(返回字符串,空格分隔)。
语法:${数组名[@/*]:起始下标:截取长度}
-
起始下标:从0开始,若为负数,从数组末尾开始计数(如
-2表示倒数第2个元素); -
截取长度:可选,若省略,截取到数组末尾。
arr=(1 2 3 4 5)
echo ${arr[@]} (输出整个数组)
1 2 3 4 5
echo ${arr[@]:0:2} (${数组名[@或*]:起始位置:长度})
1 2
echo ${arr[@]:2:3}
3 4 5
将数组切片之后,返回的是字符串,以空格作为分隔符
(2)数组替换
将数组中指定的元素内容替换为新内容,默认不修改原数组,仅返回替换后的结果;若需修改原数组,需将结果重新赋值给原数组。
语法:${数组名[@/*]/旧内容/新内容}
arr=(1 2 3 4 5)
echo ${arr[@]/4/66} (${数组名[@或*]/查找字符/替换字符})
1 2 3 66 5
echo ${arr[@]} (并不会替换数组原有内容)
1 2 3 4 5
arr=(${arr[@]/4/66}) (要改变原有数组,可通过重新赋值实现)
echo ${arr[@]}
1 2 3 66 5
(3)数组删除
通过unset命令实现,可删除单个元素或整个数组,删除单个元素后,数组下标不会自动重排。
arr=(1 2 3 4 5)
unset arr (删除数组)
echo ${arr[*]}
arr=(1 2 3 4 5)
unset arr[2] (删除第三个元素)
echo ${arr[*]}
1 2 4 5
4. 补充知识点:数组的常用拓展操作
在原文档基础上,补充3个实际开发中高频使用的数组操作,提升脚本实用性:
(1)动态添加数组元素
直接给未定义的下标赋值,即可实现元素的动态添加,无需提前声明长度。
#!/bin/bash
arr=(1 2 3)
arr[3]=4 # 添加第4个元素
arr[4]=5 # 添加第5个元素
echo "动态添加后:${arr[@]}" # 输出1 2 3 4 5
(2)数组去重
利用awk或sort+uniq命令,实现数组中重复元素的去重,适用于批量数据去重场景。
#!/bin/bash
arr=(1 2 2 3 3 3 4)
# 数组去重,重新赋值给原数组
arr=($(echo "${arr[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))
echo "去重后数组:${arr[@]}" # 输出1 2 3 4
(3)数组合并
将多个数组合并为一个新数组,直接通过()拼接即可,语法简洁。
#!/bin/bash
arr1=(1 2 3)
arr2=(4 5 6)
arr3=("a" "b")
# 合并数组
new_arr=(${arr1[@]} ${arr2[@]} ${arr3[@]})
echo "合并后数组:${new_arr[@]}" # 输出1 2 3 4 5 6 a b
三、Shell脚本调试(简易版)
编写函数和数组的复杂脚本时,难免出现语法错误或逻辑问题,Shell提供了轻量的调试方式,无需额外工具,通过bash参数或set命令即可快速定位问题,核心调试技巧如下:
1. 整脚本报错检查
直接通过bash命令的参数运行脚本,检查语法或执行过程中的错误,核心参数:
# 仅检查语法错误,不执行脚本(推荐先做语法检查)
bash -n 脚本名.sh
# 执行脚本并输出所有执行的命令(调试核心,查看执行过程)
bash -x 脚本名.sh
# 在执行脚本时,先将脚本的内容输出到屏幕上然后执行脚本,如有错误,会提示
bash -v 脚本名.sh
2. 脚本局部调试
若仅需调试脚本中的某一段代码(如函数、数组遍历),可通过set命令开启/关闭局部调试,避免输出过多无关信息:
set -x # 开启调试模式,后续命令执行时会输出详细过程
# 需调试的代码段(如函数调用、数组操作)
set +x # 关闭调试模式,后续命令恢复正常执行
3. 简易调试技巧:echo打印
在函数或数组操作的关键位置,通过echo打印变量值(如数组元素、函数参数),是最直观的调试方式,适合快速定位变量赋值、参数传递的问题。
#!/bin/bash
arr=(1 2 3)
# 调试:打印数组所有元素
echo "数组元素:${arr[@]}"
sum=0
for num in ${arr[@]}
do
sum=$((sum + num))
# 调试:打印每次循环的sum值
echo "当前sum:$sum"
done
四、核心总结
-
函数:核心是封装与复用,支持参数传递、递归调用,通过
local定义局部变量避免冲突,复杂返回值可通过echo+变量接收实现,是脚本模块化的核心。 -
数组:仅支持一维,无需定义长度,4种定义方法灵活,核心操作包括获取长度、遍历、切片、替换、删除,补充的动态添加、去重、合并是实际开发的高频技巧。
-
调试:优先用
bash -n做语法检查,再用bash -x或set -x做执行过程调试,echo打印是最简易的定位问题方式。
函数和数组的结合使用,能大幅提升Shell脚本的处理能力,例如用函数封装业务逻辑,用数组存储批量数据,再通过循环遍历数组调用函数,可实现高效的批量处理场景(如批量监控服务器、批量处理文件、批量计算数据)。
更多推荐




所有评论(0)