当前位置: 首页 > news >正文

Shell编程之函数与数组

目录

一、Shell 函数:代码组织与复用的核心

(一)函数基础:定义与调用

1. 函数定义语法

2. 函数调用方式

3. 示例:两数求和函数

(二)函数变量作用域:全局与局部变量

1. local关键字用法

2. 示例:全局与局部变量对比

(三)函数参数:传递与接收

1. 参数传递语法

2. 示例:日志写入函数

(四)递归函数:自我调用的实现

1. 示例:递归遍历目录

(五)系统资源监控函数:实战案例

1. 需求:定期监控 CPU 和内存使用率,超过阈值时报警

二、Shell 数组:高效处理批量数据

(一)数组定义:四种常见方式

1. 直接赋值(连续下标)

2. 显式指定下标

3. 从变量列表初始化

4. 逐个元素赋值

(二)数组基本操作

1. 获取数组长度

2. 读取指定下标元素

3. 遍历数组元素

4. 数组切片:提取子数组

5. 元素替换与删除

(三)稀疏数组:非连续下标处理

三、Shell 脚本调试:定位问题的关键工具

(一)echo 命令:分段排查

(二)bash 调试参数:三种模式

1. -n:语法检查(不执行脚本)

2. -v:显示脚本内容后执行

3. -x:显示执行的每一条命令

示例:调试分数判断脚本

(三)set 命令:局部调试

四、总结

(一)函数

(二)数组

(三)调试


 

一、Shell 函数:代码组织与复用的核心

(一)函数基础:定义与调用

在 Shell 脚本中,函数是将重复或独立的代码块封装成可复用单元的关键工具。通过函数,可避免代码冗余,提升脚本的可读性和模块化程度。

1. 函数定义语法

[function] 函数名() {命令序列[return x]  # 可选,返回0表示成功,非0表示错误
}
  • function关键字可选,省略后直接以函数名加括号定义。
  • 大括号{}内为函数体,包含具体执行的命令。
  • return语句用于退出函数并返回状态码(0-255),省略时默认返回最后一条命令的执行状态。

2. 函数调用方式

函数名 [参数1] [参数2] ...  # 直接使用函数名加参数列表调用

3. 示例:两数求和函数

脚本文件:sum.sh

#!/bin/bash
# 定义求和函数
sum() {echo "请输入第一个数:"read num1# 检查输入是否为整数if ! [[ $num1 =~ ^[0-9]+$ ]]; thenecho "错误:第一个数必须是整数!"return 1  # 返回错误码1fiecho "请输入第二个数:"read num2if ! [[ $num2 =~ ^[0-9]+$ ]]; thenecho "错误:第二个数必须是整数!"return 1firesult=$((num1 + num2))echo "两数之和为:$result"
}# 调用函数
sum

执行步骤:

chmod +x sum.sh  # 赋予执行权限
./sum.sh          # 运行脚本

输出结果:

请输入第一个数:
2
请输入第二个数:
3
两数之和为:5

(二)函数变量作用域:全局与局部变量

Shell 函数在当前 Shell 环境中执行,未声明的变量默认是全局变量,可被函数内外访问。若需限制变量仅在函数内有效,需使用local关键字。

1. local关键字用法

函数名() {local 变量名  # 声明局部变量,仅在函数内可见变量名=值
}

2. 示例:全局与局部变量对比

脚本文件:fun_scope.sh

#!/bin/bash
i=9  # 全局变量myfun() {local i=8  # 局部变量,与全局变量i隔离echo "函数内i:$i"
}myfun  # 调用函数
echo "函数外i:$i"  # 输出全局变量i

执行结果:

函数内i:8
函数外i:9
  • 函数内通过local声明的i为局部变量,修改不影响全局变量。
  • 未声明local的变量在函数内修改会影响全局作用域。

(三)函数参数:传递与接收

函数可接收外部传递的参数,通过$1$2...${10}(第 10 个及之后参数需加花括号)获取,类似脚本的位置参数。

1. 参数传递语法

函数名 参数1 参数2 参数3  # 调用时传递参数

2. 示例:日志写入函数

脚本文件:write_log.sh

#!/bin/bash
mydir="/data"
outfile="${mydir}/my.log"
mkdir -p "$mydir"  # 创建日志目录(若不存在)# 定义日志写入函数:第一个参数为文件路径,第二个为日志内容
appendfile() {echo "$2" >> "$1"  # 将日志内容追加到文件
}# 调用函数,传递参数
appendfile "$outfile" "第一条日志:$(date)"
appendfile "$outfile" "第二条日志:Shell函数参数示例"

执行步骤:

chmod +x write_log.sh
./write_log.sh
cat /data/my.log  # 查看日志内容

输出结果:

第一条日志:2025年4月15日 星期二 15:30:00
第二条日志:Shell函数参数示例

(四)递归函数:自我调用的实现

递归函数指函数在执行过程中调用自身,常用于遍历目录、斐波那契数列等场景。

1. 示例:递归遍历目录

脚本文件:fun_recursion.sh

#!/bin/bash
# 定义递归函数:遍历指定目录下的所有文件和子目录
traverse_directory() {local dir=$1  # 当前目录路径for item in "$dir"/*; do  # 遍历当前目录下的所有项目if [ -d "$item" ]; then  # 如果是目录echo "目录:$item"traverse_directory "$item"  # 递归调用自身,遍历子目录elif [ -f "$item" ]; then  # 如果是文件echo "文件:$item"fidone
}# 从当前目录开始遍历
traverse_directory "."

执行结果:

目录:./docs
文件:./docs/README.md
目录:./scripts
文件:./scripts/sum.sh
文件:./scripts/fun_scope.sh
  • 通过for循环遍历目录项,递归处理子目录,实现层级遍历。
  • 注意递归终止条件:当遍历到文件时不再递归,避免无限循环。

(五)系统资源监控函数:实战案例

1. 需求:定期监控 CPU 和内存使用率,超过阈值时报警

脚本文件:jiankong.sh

#!/bin/bash
# 发送报警信息的函数
send_alert() {local message=$1echo "ALERT: $message"  # 实际可扩展为发送邮件/短信
}# 监控系统资源的函数:参数依次为CPU阈值、内存阈值、监控间隔(秒)
monitor_system_resources() {local cpu_threshold=$1local mem_threshold=$2local interval=$3while true; do# 获取CPU使用率(用户+系统占用率)cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{printf "%.2f", $2 + $4}')# 获取内存使用率(已用内存/总内存*100)mem_usage=$(free | awk '/Mem/ {printf "%.2f", $3/$2 * 100}')# 比较CPU使用率if (( $(echo "$cpu_usage > $cpu_threshold" | bc -l) )); thensend_alert "CPU使用率 $cpu_usage% 超过阈值 $cpu_threshold%!"fi# 比较内存使用率if (( $(echo "$mem_usage > $mem_threshold" | bc -l) )); thensend_alert "内存使用率 $mem_usage% 超过阈值 $mem_threshold%!"fisleep "$interval"  # 等待指定间隔done
}# 启动监控:CPU阈值80%,内存阈值85%,间隔5秒
monitor_system_resources 80 85 5

关键技术点:

  • top -bn1:非交互模式获取单次系统状态,避免交互式界面。
  • bc -l:处理浮点数比较,需安装bc工具(yum install bcapt-get install bc)。
  • while true:无限循环实现持续监控,可通过Ctrl+C终止。

二、Shell 数组:高效处理批量数据

Shell 数组是存储多个数据的集合,支持一维数组,下标从 0 开始,元素用空格分隔。

(一)数组定义:四种常见方式

1. 直接赋值(连续下标)

数组名=(value0 value1 value2 ...)
# 示例
arr1=(1 2 3 4 5)  # 下标0-4,元素1-5

2. 显式指定下标

数组名=([0]=value [1]=value [2]=value ...)
# 示例
arr2=([0]="apple" [2]="banana" [3]="orange")  # 下标0、2、3有值,1为空

3. 从变量列表初始化

列表="value0 value1 value2"
数组名=($列表)
# 示例
list="hello world shell"
arr3=($list)  # arr3=(hello world shell)

4. 逐个元素赋值

数组名[0]="value"
数组名[1]="value"
# 示例
arr4[0]="red"
arr4[1]="green"
arr4[2]="blue"

(二)数组基本操作

1. 获取数组长度

${#数组名[@]} 或 ${#数组名[*]}  # 两种语法等价
# 示例
arr=(1 2 3 4 5)
echo "数组长度:${#arr[@]}"  # 输出5

2. 读取指定下标元素

${数组名[下标]}  # 下标从0开始,负数表示从末尾倒数(如-1为最后一个元素)
# 示例
echo "第三个元素:${arr[2]}"  # 输出3
echo "最后一个元素:${arr[-1]}"  # 输出5(Bash 4.0+支持负下标)

3. 遍历数组元素

for 变量 in ${数组名[@]}; do操作
done
# 示例脚本:array_traverse.sh
#!/bin/bash
arr=(1 2 3 4 5)
for value in "${arr[@]}"; do  # 加引号避免空格分割问题echo "元素:$value"
done

执行结果:

元素:1
元素:2
元素:3
元素:4
元素:5

4. 数组切片:提取子数组

语法:${数组名[@]:起始下标:长度}

arr=(1 2 3 4 5 6)
echo "前两个元素:${arr[@]:0:2}"  # 输出1 2
echo "从下标2开始取3个元素:${arr[@]:2:3}"  # 输出3 4 5

5. 元素替换与删除

  • 替换(不修改原数组):
    echo "${arr[@]/旧值/新值}"  # 替换第一个匹配项
    echo "${arr[@]//旧值/新值}"  # 替换所有匹配项
    
  • 修改原数组:
    arr=(${arr[@]/旧值/新值})  # 重新赋值
    
  • 删除元素:
    unset arr[下标]  # 删除指定下标元素
    unset arr  # 删除整个数组
    

示例:

arr=(1 2 3 4 3 5)
echo "替换第一个3为6:${arr[@]/3/6}"  # 输出1 2 6 4 3 5
echo "替换所有3为6:${arr[@]//3/6}"  # 输出1 2 6 4 6 5
arr=(${arr[@]//3/6})  # 原数组变为(1 2 6 4 6 5)
unset arr[2]  # 删除下标2的元素(6)
echo "删除后数组:${arr[@]}"  # 输出1 2 4 6 5

(三)稀疏数组:非连续下标处理

Shell 允许数组下标不连续,未赋值的下标默认为空。

arr=([0]="a" [3]="b")  # 下标0和3有值,1、2为空
echo "数组所有下标:${!arr[@]}"  # 输出0 3(${!数组名[@]}获取所有下标)
echo "下标1的值:${arr[1]}"  # 输出空

三、Shell 脚本调试:定位问题的关键工具

编写脚本时难免出错,调试工具可帮助快速定位语法或逻辑错误。

(一)echo 命令:分段排查

在可能出错的代码段插入echo语句,输出变量值或执行状态。

#!/bin/bash
read -p "输入数字:" num
echo "输入的数字是:$num"  # 调试用,检查输入是否正确
if [ $num -gt 10 ]; thenecho "大于10"
elseecho "小于等于10"
fi

(二)bash 调试参数:三种模式

通过shbash命令带参数执行脚本,开启调试模式。

1. -n:语法检查(不执行脚本)

sh -n script.sh  # 仅检查语法,不报逻辑错误
  • 输出:若有语法错误(如未闭合的引号、缺少fi),会提示具体位置。

2. -v:显示脚本内容后执行

sh -v script.sh  # 先打印脚本每一行,再执行
  • 适合查看脚本是否按预期读取文件或变量。

3. -x:显示执行的每一条命令

sh -x script.sh  # 打印每条执行的命令及其参数
  • 输出带+前缀的命令,便于追踪逻辑错误。

示例:调试分数判断脚本

脚本文件:test.sh

#!/bin/bash
read -p "请输入分数(0-100):" GRADE
if [ $GRADE -ge 85 ] && [ $GRADE -le 100 ]; thenecho "优秀"
elif [ $GRADE -ge 70 ] && [ $GRADE -le 84 ]; thenecho "合格"
elseecho "不合格"
fi

调试执行:

sh -x test.sh  # 输出每条命令的执行过程

输出片段:

+ read -p '请输入分数(0-100):' GRADE
请输入分数(0-100):75
+ [ 75 -ge 85 ]
+ [ 75 -ge 70 ] && [ 75 -le 84 ]
+ echo 合格
合格

(三)set 命令:局部调试

在脚本中使用set -x开启调试,set +x关闭,仅调试部分代码。

#!/bin/bash
set -x  # 开启调试
echo "开始处理"
read -p "输入内容:" input
set +x  # 关闭调试
echo "输入的内容是:$input"

执行结果:

+ echo 开始处理
开始处理
+ read -p 输入内容: input
输入内容: hello
输入的内容是: hello

四、总结

(一)函数

  • 复用性:避免重复代码,提升开发效率。
  • 可读性:将复杂逻辑拆分为独立函数,结构清晰。
  • 灵活性:支持参数传递、递归调用,适应多样化场景。

(二)数组

  • 高效存储:无需预先定义长度,动态扩展。
  • 丰富操作:切片、替换、删除等功能满足数据处理需求。
  • 遍历便利:结合循环快速处理数组元素。

(三)调试

  • 语法检查-n参数快速定位语法错误。
  • 执行追踪-x参数显示命令执行流程,排查逻辑问题。
  • 分段调试set命令灵活控制调试范围,避免全脚本输出冗余信息。

 

相关文章:

  • 临床协调简历模板
  • python——学生管理系统
  • 5款电脑健康状况监测软件
  • STL详解 - vector的模拟实现
  • AI核心概念之“RAG” - 来自DeepSeek
  • curl命令
  • AI大模型与行业变革:从传统到智能的跃迁之路
  • [net 5] udp_dict_server 基于udp的简单字典翻译(服务器与业务相分离)
  • leetcode 121. Best Time to Buy and Sell Stock
  • 编译原理(自考13007)
  • OpenAI GPT-4.1系列模型的突破与潜力
  • emotn ui桌面软件tv版下载安装教程-emotn ui桌面好用吗
  • 【触想智能】工业触摸一体机在金融智能设备领域上应用的优势
  • 实战指南:封装Whisper为FastAPI接口并实现高并发处理
  • 华为云CloudMatrix 384 超节点将有数万规模上线,赋能AI产业发展
  • 锂电池行业碳酸锂结晶介质釜底阀-耐磨V型调节切断球阀:技术革新与应用解析-耀圣
  • JVM 常用监控工具介绍和使用
  • ThinkPHP中Redis操作示例
  • 重温hot100-day5
  • libevent服务器附带qt界面开发(附带源码)
  • 马上评|京东VS美团,消费者希望看到的不是“口水仗”
  • 中共中央、国务院印发《关于实施自由贸易试验区提升战略的意见》
  • “6+2”小复式追加票!松江购彩者擒大乐透1672万头奖
  • 2025年度“沪惠保”将于4月22日开售,保费不变
  • 重大虚开发票偷税骗补案被查处:价税2.26亿,涉700余名主播
  • 85岁眼科专家、武汉大学人民医院原眼科主任喻长泰逝世