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

Shell 函数详解

Shell 函数详解

函数基础:定义与调用

1.1 函数定义格式

Shell 函数有两种标准定义格式,核心是 “将重复执行的命令序列封装为一个命名模块”,两种格式功能完全一致,仅语法风格不同

格式类型语法结构说明
function 关键字型bash function 函数名 { 命令序列 } bash function 函数名() { 命令序列 }显式用 function 声明,可读性强,推荐初学者使用;() 可加可不加。
简化括号型bash 函数名() { 命令序列 }省略 function,仅用 () 标识函数,语法更简洁,是实际开发中的主流写法。

示例:定义一个 “打印欢迎信息” 的函数

[root@syf ~]# vim y.sh
#!/bin/bash
function welcome(){echo "================="echo "欢迎使用Shell函数"echo "================="
}
hello() {echo "Hello! 当前时间:$(date +%H:%M:%S)"
}
welcome
hello
~  

执行结果

[root@syf ~]# ./y.sh 
=================
欢迎使用Shell函数
=================
Hello! 当前时间:19:34:11

1.2 函数调用规则

  1. 调用时机:函数必须 “先定义,后调用”—— 脚本中函数定义需放在调用代码之前,否则会报错 “命令未找到”。
  2. 调用方式:直接输入函数名(如 welcome),无需加 ()(与其他编程语言不同)。
  3. 参数传递:调用时可跟参数(如 sum 10 20),函数内部通过 $1$2… 读取(类似脚本的位置参数)。

函数返回值:两种核心方式

Shell 函数的 “返回值” 有两种场景:退出状态码(用 return)实际结果(用 echo),需根据需求选择,避免混淆

2.1 rturn:返回退出状态码(0-255)

  • 作用:标识函数执行结果的 “成功 / 失败”,或返回小范围整数(0-255),脚本中用 $? 读取
  • 规则:
    1. return 后的值必须在 0-255 之间,超出则自动对 256 取余(如 257 → 1,250 → 244,256 → 0)
    2. $? 仅保存 “最后一条命令 / 函数” 的退出状态码,需在函数调用后立即读取,否则会被后续命令覆盖
    3. 0 表示 “执行成功”,非 0 表示 “执行失败”(遵循 Shell 命令的通用约定)

示例:用 return 返回整数的 2 倍(验证范围限制)

[root@syf ~]# vim e.sh
#!/bin/bash
function sy {read -p "请输入任意一个整数值:" nreturn $[$n*2]
}
sy
echo "$?"
~ 

执行结果(验证范围):

[root@syf ~]# ./e.sh     # 输入 40 → 40*2=80(在0-255内)
请输入任意一个整数值:40     请输入一个整数值:40
80                       函数返回值($?):80
[root@syf ~]# ./e.sh     # 输入 250 → 250*2=500 → 500%256=244
请输入任意一个整数值:250    请输入一个整数值:250
244                      函数返回值($?):244
[root@syf ~]# ./e.sh     # 输入 256 → 256*2=512 → 512%256=0 
请输入任意一个整数值:256    请输入一个整数值:256
0                        函数返回值($?):0

2.2 echo:返回实际结果(推荐)

  • 作用:当需要返回 “超出 0-255 的整数”“字符串” 或 “复杂计算结果” 时,用 echo 输出结果,脚本中用 命令替换($(函数名) 或 函数名 读取
  • 优势:无范围限制,支持任意类型的结果返回,是实际开发中传递函数结果的主流方式

示例:用 echo 返回两数之和(无范围限制)

[root@syf ~]# vim u.sh
#!/bin/bash
sum1 () {sum=$[ $1 + $2 ]echo $sum
}
sum1 $1 $2
~   

执行结果

[root@syf ~]# ./u.sh 100 200
300

2.3 关键区别总结

返回方式适用场景范围限制读取方式
return标识成功 / 失败、返回小整数0-255$?(调用后立即读取)
echo返回任意结果(整数 / 字符串)无限制命令替换($(函数名)

函数传参与变量作用域

3.1 函数传参:用位置参数传递

Shell 函数不支持 “显式参数列表”(如 add(a,b)),需通过 位置参数 传递:

  • 函数调用时,参数跟在函数名后(如 sum 10 20
  • 函数内部,用 $1 读取第一个参数,$2 读取第二个参数,$# 读取参数个数,$* 读取所有参数

示例:传递两个参数,计算和与积

[root@syf ~]# vim w.sh
#!/bin/bash
sum1 () {sum=$[ $1 + $2 ]echo $sum
}
sum1 $1 $2
~ 

执行结果

[root@syf ~]# ./w.sh 20 30
50
[root@syf ~]# ./w.sh 1 6
7

3.2 变量作用域:局部变量与全局变量

Shell 脚本中变量默认是 全局变量(整个脚本可见,包括函数内部);若需限制变量仅在函数内生效,需用 local 关键字声明为 局部变量

3.2.1 局部变量:local 关键字
  • 语法local 变量名=值(必须在函数内部声明)。
  • 特性:仅在当前函数内可见,函数外无法访问;若与全局变量同名,函数内优先使用局部变量(屏蔽全局变量)。

示例 1:基础局部变量

[root@syf ~]# vim o.sh
#!/bin/bash
abc () {echo "函数内的未经过local的变量i值$i"local ii=6echo "函数内的变量i值是$i"
}
i=9
abc
echo "函数外面的变量i值是$i"~  

执行结果

[root@syf ~]# ./o.sh 
函数内的未经过local的变量i值9
函数内的变量i值是6
函数外面的变量i值是9

local的进击:

#!/bin/bash
abc () {echo "inside1 $i"let i++local ii=8echo "inside2 $i"
}
i=9
abc
echo "outside $i"
~  

执行结果

[root@syf ~]# ./o.sh 
inside1 9
inside2 8
outside 10

3.3 注意事项

  1. 局部变量声明位置local 必须在函数内部声明,且建议在函数开头声明,避免 “先使用后声明” 的逻辑混乱。
  2. 参数与变量区分:函数内的 $1$2 是 “函数参数”,不是 “脚本参数”—— 脚本参数在函数外通过 $1 读取,函数内的 $1 仅对应函数调用时传递的参数。

函数进阶:递归与函数库

4.1 递归函数:函数调用自身

递归是 “函数调用自身” 的编程技巧,核心是 明确终止条件(避免无限递归),适用于 “分治问题”(如阶乘、斐波那契数列、目录遍历)

示例:递归计算阶乘(n! = n × (n-1) × … × 1)
[root@syf ~]# vim a.sh
#!/bin/bash
function cy() {if [ $1 -eq 1 ];thenecho 1elselocal temp=$[ $1 - 1 ]local result=`cy $temp`echo $[ result * $1 ]fi
}
read -p "输入一个值:" vaule
result=`cy $vaule`
echo "阶乘的值为: $result"
~  

执行结果

[root@syf ~]# ./a.sh 
输入一个值:5
阶乘的值为: 120             # 5! = 5×4×3×2×1=120
[root@syf ~]# ./a.sh 
输入一个值:3
阶乘的值为: 6               # 3! = 3×2×1=6
递归关键原则
  1. 必须有终止条件:如示例中 $1 -eq 1 时直接返回 1,否则会无限递归导致脚本崩溃(栈溢出)
  2. 问题规模递减:每次递归调用时,参数需 “缩小问题规模”(如 n→n-1),确保最终触发终止条件

4.2 函数库:封装复用函数

当多个脚本需要使用相同的函数时,可将这些函数集中到一个 “函数库文件” 中,其他脚本通过 source. 命令调用函数库,实现 “一次定义,多次复用”

步骤 1:创建函数库文件(如 b.sh

封装常用的数学运算函数(加、减、乘、除):

[root@syf ~]# vim b.sh
#!/bin/bash
#ee.sh函数库
jia() {result=$[ $1 + $2 ]echo "$result"
}jian() {result=$[ $1 - $2 ]echo "$result"
}cheng() {result=$[ $1 * $2 ]echo "$result"
}chu() {if [ $2 -ne 0 ];thenresult=$[ $1 / $2 ]echo "$result"elseecho "除法中分母不能为0"fi
}
步骤 2:调用函数库(如 b.sh

通过 source 函数库路径. 函数库路径 加载函数库,然后调用其中的函数:

[root@syf ~]# vim c.sh
#!/bin/bash
. /root/b.shread -p "请输入第一个数字:" n
read -p "请输入第二个数字:" mresult1=`jia $n $m`
result2=`jian $n $m`
result3=`cheng $n $m`
result4=`chu $n $m`echo "两数之和为:$result1"
echo "两数之差为:$result2"
echo "两数之乘为:$result3"
echo "两数之除为:$result4"
~ 
步骤 3:执行脚本
[root@syf ~]# chmod +x c.sh 
[root@syf ~]# ./c.sh 
[root@syf ~]# vim c.sh
[root@syf ~]# ./c.sh 
请输入第一个数字:10
请输入第二个数字:3
两数之和为:13
两数之差为:7
两数之乘为:30
两数之除为:3
[root@syf ~]# ./c.sh 
请输入第一个数字:10
请输入第二个数字:0
两数之和为:10
两数之差为:10
两数之乘为:0
两数之除为:除法中分母不能为0
函数库使用注意事项
  1. 路径问题:加载函数库时,推荐用 绝对路径(如 /root/math_lib.sh),避免脚本在不同目录执行时找不到函数库
  2. 权限问题:函数库文件无需加执行权限(chmod +x),因它是被 source 加载到当前 Shell 环境,不是作为独立脚本执行
  3. 命名冲突:若调用脚本中的函数与函数库中的函数同名,会优先使用 “后定义” 的函数(建议函数名加前缀,如 math_add,避免冲突)

总结

知识点核心要点
函数定义两种格式:function 名() { ... }名() { ... };先定义后调用
返回值return 返退出状态码(0-255),echo 返实际结果(无限制,推荐)
传参用位置参数 $1$2 传递;函数内参数与脚本参数独立
变量作用域默认全局,local 声明局部变量(仅函数内生效)
递归需有终止条件 + 问题规模递减;适用于分治问题(如阶乘)
函数库集中封装复用函数,用 source. 加载;推荐绝对路径,避免权限问题

Shell 函数的核心价值是 “代码复用” 和 “逻辑拆解”—— 将重复代码封装为函数,可减少冗余、提升维护效率;将复杂逻辑拆分为多个函数,可让脚本结构更清晰。实际开发中,结合 “函数库” 和 “递归”,可应对大部分 Shell 脚本的模块化需求


文章转载自:

http://ONSNOCEV.qtryb.cn
http://fUTTDRQc.qtryb.cn
http://XR9MSacE.qtryb.cn
http://BS3duKH7.qtryb.cn
http://S4EhdjwF.qtryb.cn
http://4wWfUujM.qtryb.cn
http://sr2bj8dc.qtryb.cn
http://uc3fTDuh.qtryb.cn
http://dNDxNod6.qtryb.cn
http://jYsbuR3p.qtryb.cn
http://mYBgSpnG.qtryb.cn
http://Yh14kudM.qtryb.cn
http://vw3uD6h4.qtryb.cn
http://5zUEKOm2.qtryb.cn
http://m5oRUoNc.qtryb.cn
http://vF6saQFh.qtryb.cn
http://ENKJzUZR.qtryb.cn
http://rXJuyzxW.qtryb.cn
http://3I3Eg8Aa.qtryb.cn
http://bkhVcTMP.qtryb.cn
http://IrT5uest.qtryb.cn
http://pk1ObX4j.qtryb.cn
http://Neadigv6.qtryb.cn
http://mUCmPMLu.qtryb.cn
http://fe4NxrZI.qtryb.cn
http://HcHUPkUB.qtryb.cn
http://u2OuJTY4.qtryb.cn
http://ZG8Y1t04.qtryb.cn
http://yd2g5jsy.qtryb.cn
http://VCYbqrBA.qtryb.cn
http://www.dtcms.com/a/377490.html

相关文章:

  • 【系统分析师】第21章-论文:系统分析师论文写作要点(核心总结)
  • Linux 命令(top/ps/netstat/vmstat/grep/sed/awk)及服务管理(systemd)
  • 【图像生成】提示词技巧
  • 揭秘Linux:开源多任务操作系统的强大基因
  • (ICLR-2025)深度压缩自动编码器用于高效高分辨率扩散模型
  • 《Why Language Models Hallucinate》论文解读
  • 【机器学习】通过tensorflow实现猫狗识别的深度学习进阶之路
  • AD5362BSTZ电子元器件 ADI 高精度数字模拟转换器DAC 集成电路IC
  • DMA-M2M存储器与存储器之间读写
  • Mistral Document AI已正式登陆Azure AI Foundry(国际版)
  • 机器学习实战(二):Pandas 特征工程与模型协同进阶
  • Flutter 朦胧效果布局大全:5种方法实现优雅视觉层次
  • 【CVPR2023】奔跑而非行走:追求更高FLOPS以实现更快神经网络
  • PHP学习(第三天)
  • 数仓简要笔记-1
  • 机器人商业化落地需要突破的关键性技术
  • AI 技术体系核心概念
  • STM32H750 I2C介绍及应用
  • 计算机网络---物理层
  • 【freemarker】创建html页面
  • 【华为OD】区块链文件转储系统
  • sprintf不是像printf一样的打印函数吗
  • Js 图片加载完成 与 图片缓存加载的区别
  • 汽车动力电池管理系统(BMS):电动汽车的“智能大脑”
  • n8n add npm module 發生 Module ‘ioredis‘ is disallowed,getaddrinfo EAI_AGAIN
  • 性能——day3
  • 安卓学习 之 SeekBar(音视频播放进度条)
  • CRMEB标准版PHP订单列表功能详解与优化技巧
  • Linux基础知识(五)
  • [数据结构——lesson7.队列]