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

【运维进阶】Shell 函数的知识与实践

Shell 函数的知识与实践

Shell 函数介绍

  • 定义方式:Shell 函数用于封装一段可重复执行的代码,有两种基本定义形式,一种是通过 “函数名 () { 命令 }”,另一种是使用 “function 函数名 { 命令 }”。

  • 下面是Shell 函数的常见语法格式。

    标准写法

    function 函数名 () {指令...return n
    }
    

    简化写法1:不写()

    function 函数名 {指令...return n
    }
    

    简化写法2:不写function

    函数名 () {指令...return n
    }
    
  • 核心作用:主要是实现代码复用,避免重复编写相同逻辑;将复杂脚本拆分为多个功能单元,提升模块化程度;通过清晰的函数名,增强脚本的可读性和可维护性。

  • 调用方法:调用时直接使用函数名,若有参数,在函数名后依次添加参数即可,无需额外的括号包裹。

  • 参数与返回值:函数可通过$1 $2等位置参数接收外部传入的参数,通过$#获取参数数量;返回值默认是最后一条命令的退出状态,也可通过return关键字显式指定(范围为 0-255)。

  • 变量作用域:函数内部定义的变量默认是全局的,在函数内外都能访问;若要定义仅在函数内部有效的变量,可使用local关键字。

Shell 函数的基础实践

**示例1:**hello函数

# 想象就像拍电影,得先确定演员和剧本,才能喊开始拍。
# 第一个脚本 fun1.sh 是正确的流程:
[lth@controller ~ 11:21:45]$ cat fun1.sh 
#!/bin/bash
# 先确定演员和表演内容(定义函数)
function hello () {echo "Hello World !"  # 演员的台词
}
# 一切准备就绪,让演员上场表演(调用函数)
hello# 执行后演员顺利出场,说出了台词:Hello World !
[lth@controller ~ 11:21:57]$ bash fun1.sh 
Hello World !# 第二个脚本 fun2.sh 就乱了套:
[lth@controller ~ 11:38:07]$ cat fun2.sh
#!/bin/bash
# 还没确定谁来演,就直接喊开始(先调用函数)
hello
# 这时候才确定演员和剧本(后定义函数)
function hello () {echo "Hello World !"
}
# 结果自然是找不到演员,报错:fun2.sh: line 2: hello: command not found(找不到叫 hello 的演员)
[lth@controller ~ 11:38:16]$ bash fun2.sh
fun2.sh: line 2: hello: command not found
# 所以 Bash 这个 "导演" 很较真,必须按 "先定角色,再让角色出场" 的顺序来~

**示例2:**调用外部函数

# 首先,第一部分是创建 "工具库" 的过程:
[lth@controller ~ 20:57:37]$ cat >> mylib << 'EOF'
function hello () {echo "Hello World !"
}
EOF
# 这一步就像编写了一本叫mylib的工具手册,里面定义了一个hello函数 —— 就像记录了一套 "打招呼流程",执行它就会输出 "Hello World !"# 然后是主程序fun3.sh的内容:
[lth@controller ~ 20:57:41]$ cat fun3.sh 
#!/bin/bash
if [ -r mylib ];thensource mylib
elseecho mylib is not exist exit 1
fi
hello# 当运行脚本时:
[lth@controller ~ 20:57:54]$ bash fun3.sh 
Hello World !
# 整个过程顺利完成:找到了工具库→加载了函数→成功执行→输出了结果,完美展现了 "模块化调用" 的编程思想。

**示例3:**带参数的函数

# 首先是脚本`fun4.sh`的内容:
[lth@controller ~ 21:17:30]$ cat fun4.sh 
#!/bin/bash
function print () {if [ "$1" == "PASS" ];thenecho -e '\033[1;32mPASS\033[0;39m'elif [ "$1" == "FAIL" ];thenecho -e '\033[1;31mFAIL\033[0;39m'elif [ "$1" == "DONE" ];thenecho -e '\033[1;35mDONE\033[0;39m'elseecho "Usage: print PASS|FAIL|DONE"fi
}
read -p "请输入你想要打印的内容:" str
print $str# 这个脚本的工作流程就像一场互动表演:#  首先定义了一个叫`print`的 "染色播报员" 函数,它认识三种特殊指令:
#   - 收到 "PASS" 就用绿色高亮显示(`\033[1;32m`是绿色代码)
#   - 收到 "FAIL" 就用红色高亮显示(`\033[1;31m`是红色代码)
#   - 收到 "DONE" 就用紫色高亮显示(`\033[1;35m`是紫色代码)
#   - 遇到不认识的指令,就会提示正确用法
#  然后脚本会友好地问用户:"请输入你想要打印的内容:"(`read -p`实现)
#  最后把用户输入的内容传给 "染色播报员" 去处理(`print $str`)# 当我们运行脚本并输入 "PASS" 时:
[lth@controller ~ 21:17:58]$ bash fun4.sh 
请输入你想要打印的内容:PASS
PASS
# 此时 "播报员" 会用绿色高亮显示 "PASS"(只是这里文本无法显示颜色效果)# 如果输入不认识的内容,比如 "hello":
[lth@controller ~ 21:19:28]$ bash fun4.sh 
请输入你想要打印的内容:hello
Usage: print PASS|FAIL|DONE
# "播报员" 就会礼貌地告诉你它能处理哪些指令,非常尽职尽责!

**示例4:**hello函数

#这段代码就像一个 "参数传递小剧场",让我们看看其中的角色和剧情:#首先是脚本`fun5.sh`的内容:
[lth@controller ~ 21:24:40]$ cat fun5.sh 
#!/bin/bash
function print () {if [ "$1" == "PASS" ];thenecho -e '\033[1;32mPASS\033[0;39m'elif [ "$1" == "FAIL" ];thenecho -e '\033[1;31mFAIL\033[0;39m'elif [ "$1" == "DONE" ];thenecho -e '\033[1;35mDONE\033[0;39m'elseecho "Usage: $0 PASS|FAIL|DONE"
fi
}
str=$2
print $str
# 这个脚本的角色关系很有趣:# 首先还是那个熟悉的 "染色播报员" 函数`print`,它能根据收到的指令输出不同颜色的文字
# 但这次脚本增加了 "参数传递员" 的角色:`str=$2`表示把脚本收到的第二个参数交给变量`str`
# 最后让 "播报员" 处理这个`str`变量:`print $str`# 当我们运行脚本并传入两个参数时:
[lth@controller ~ 21:25:34]$ bash fun5.sh PASS FAIL
FAIL
# 这里的剧情是:# 脚本收到两个 "包裹":第一个是`PASS`($1),第二个是`FAIL`($2)
# "传递员" 只取了第二个包裹`FAIL`给`str`
# "播报员" 收到`FAIL`,于是输出红色的`FAIL`# 当我们不带参数运行时:
# 结果表明,$0 仍然使用脚本名,而非函数名。
[lth@controller ~ 21:25:51]$ bash fun5.sh 
Usage: fun5.sh PASS|FAIL|DONE
# 整个脚本展示了 shell 中参数传递的规则:函数有自己的参数(`$1`代表函数收到的第一个参数),脚本也有自己的参数(`$2`代表脚本收到的第二个参数),它们就像不同角色的 "专属邮箱",互不干扰。

函数的递归调用

示例1:求1+2+3+…+10 的和
[lth@controller bin 22:07:05]$ cat num1.sh
#!/bin/bash
# 定义了一个叫sum_out的函数,它能计算从1加到某个数的总和
function sum_out() {# 如果输入的数字是1,那总和自然就是1(这是递归的"终点")if [ $1 -eq 1 ];thensum=1else# 否则,总和就是当前数字加上"1到当前数字减1的总和"# 这里调用了自身(sum_out $[ $1 - 1 ]),就像把问题拆成更小的同类问题sum=$[ $1 + $(sum_out $[ $1 - 1 ] ) ]fi# 输出计算好的总和echo $sum
}
# 友好地让用户输入一个整数
read -p "输入一个你想计算和的整数:" num
# 调用sum_out函数,让它计算从1加到用户输入的数字的总和
sum_out $num# 执行
[lth@controller bin 22:07:11]$ chmod +x num1.sh 
[lth@controller bin 22:07:28]$ bash num1.sh 
输入一个你想计算和的整数:9
45

这个脚本的工作方式很像剥洋葱:比如计算 1 到 5 的和,它会先问 “1 到 4 的和是多少”,计算 1 到 4 的和时又会问 “1 到 3 的和是多少”… 一直问到 “1 到 1 的和是多少”(答案是 1),然后再一层层把结果加回来,最终得到 1+2+3+4+5 的总和。这种自己调用自己解决问题的方式,就是编程中很有意思的 “递归” 思想。

示例2:求1*2*3*…*10 的阶乘
[lth@controller bin 22:12:29]$ cat num2.sh
#!/bin/bash
# 这是一个计算阶乘的函数,名叫fact_out,阶乘就像数字的"连环乘法"游戏
function fact_out() {# 当输入的数字是1时,阶乘就是1(这是游戏的终点,1的阶乘规定为1)if [ $1 -eq 1 ];thensum=1else# 否则,这个数字的阶乘 = 它自己 × 比它小1的数字的阶乘# 这里自己调用自己,就像剥洋葱,一层层把问题变小sum=$[ $1 * $(fact_out $[ $1 - 1 ] ) ]fi# 把算好的阶乘结果"喊"出来echo $sum
}
# 友好地请用户输入一个整数(其实是要算这个数的阶乘,提示文字里写"和"是笔误哦)
read -p "输入一个你想计算和的整数:" num
# 调用fact_out函数,让它计算用户输入数字的阶乘
fact_out $num# 执行
[lth@controller bin 22:12:34]$ chmod +x num2.sh 
[lth@controller bin 22:13:06]$ bash num2.sh 
输入一个你想计算和的整数:5
120

这个脚本就像一个 “阶乘计算器”,比如算 5 的阶乘时,它会这样思考:
5 的阶乘 = 5 × 4 的阶乘
4 的阶乘 = 4 × 3 的阶乘
3 的阶乘 = 3 × 2 的阶乘
2 的阶乘 = 2 × 1 的阶乘
1 的阶乘 = 1
然后从最里面的 1 开始往外算:1→2→6→24→120,最后得出 5 的阶乘是 120。这种自己调用自己的 “递归” 方式,完美贴合了阶乘的数学定义呢!

示例3:fork 炸弹(太危险我就不演示了)
:(){ :|:& };:

解释如下:

:()   # 定义函数,函数名为":",即每当输入":"时就会自动调用{}内代码
{        # ":"函数起始字元:    # 用递归方式调用":"函数本身|    # 使用管道一次产生两个函数调用:    # 另一次递归调用的":"函数&    # 放后台运行,以便产生更多的子进程
}        # ":"函数终止
;        # ":"函数定义结束后将要进行的操作...
:        # 调用":"函数,"引爆"fork炸弹

fork 炸弹原因:无限制的启用新进程,直到消耗完所有计算资源。

解决办法:限制用户进程数量。

例如:限制100个进程

[lth@controller bin 22:13:16]$ ulimit -u 100
http://www.dtcms.com/a/342745.html

相关文章:

  • CTFSHOW | 其他篇题解(一)web396-web416
  • 学习日志39 python
  • 华为iVS1800接入SVMSPro平台
  • Web3 的发展挑战:技术、监管与生态的多重困境
  • 使用C++11改进工厂方法模式:支持运行时配置的增强实现
  • 【Ansible】将文件部署到受管主机1:文件模块
  • Autoware Universe 感知详解 | 第二节 宏观认识Autoware Universe感知模块整体架构
  • C++中的内存管理(二)
  • 第四章:大模型(LLM)】07.Prompt工程-(6)受限生成和引导生成
  • 机械试验台功能量具平台:铸铁工装平台
  • 阿里云对象存储OSS之间进行数据转移教程
  • 小迪安全v2023学习笔记(六十八讲)—— Java安全原生反序列化SpringBoot攻防
  • iOS沙盒机制
  • 【系统信息相关】datecal命令
  • React + Antd+TS 动态表单容器组件技术解析与实现
  • (栈)Leetcode155最小栈+739每日温度
  • Python爬虫实战:研究puzzle,构建谜题类数据采集分析系统
  • 编程语言与存储过程:业务处理的速度与取舍
  • 3ds Max 渲染动画总模糊?
  • 基于stm32的智能建筑能源管理系统/基于单片机的能源管理系统
  • 【Java SE】认识数组
  • 【Protues仿真】基于AT89C52单片机的舵机和直流电机控制
  • 【新启航】3D 扫描逆向抄数全流程工具与技能:从手持设备到 CAD 建模的 10 项核心配置解析
  • windows10安装playwright
  • Workerman在线客服系统源码独立部署
  • 笔记本电脑Windows+Ubuntu 双系统,Ubuntu无法挂载Windows的硬盘 报错问题解决
  • TDengine IDMP 运维指南(常见问题)
  • 天眼应急案例(二)
  • 一句话生成uml图相关操作
  • MTK平台蓝牙学习-- 如何查看蓝牙连接参数