函数,数组与正则表达式

发布于:2025-09-02 ⋅ 阅读:(17) ⋅ 点赞:(0)

Bash 进阶实战:函数、数组与正则表达式全解析

在 Linux 系统管理和自动化运维中,Bash 脚本是不可或缺的工具。掌握函数、数组和正则表达式这三大核心特性,能让你的脚本从“简单指令堆砌”升级为“模块化、高效化的工具”。本文将结合实例,详细讲解这三大特性的用法,帮你夯实 Bash 进阶基础。

一、Bash 自定义函数:让代码更模块化

函数的核心价值是“代码复用”——把重复执行的逻辑封装成独立模块,既减少冗余,又便于维护。Bash 中函数的定义和调用都非常简洁,且支持参数传递与返回值处理。

1.1 函数的定义格式

Bash 函数有两种常用定义方式,推荐使用第一种(结构更清晰):

# 方式1:标准格式(推荐)
function 函数名() {
    命令序列          # 函数体:可包含普通命令、流程控制(if/for等)
}

# 方式2:简化格式(省略 function 关键字)
函数名() {
    命令序列
}

注意事项

  • 函数名需符合“字母/数字/下划线”规则,且不能以数字开头(如 sum1 合法,1sum 非法);
  • 函数体的 {} 前后需留空格(或换行),否则会语法报错;
  • 函数必须先定义,再调用(Bash 按顺序执行脚本,未定义的函数无法调用)。

1.2 函数的调用:直接用函数名

调用函数无需加括号,直接写“函数名”即可。如果需要传递参数,在函数名后紧跟参数(空格分隔)。

示例1:无参数函数
#!/bin/bash
# 定义一个打印欢迎信息的函数
function print_welcome() {
    echo "====================="
    echo " 欢迎使用 Bash 工具 "
    echo "====================="
}

# 调用函数(直接写函数名)
print_welcome

执行结果:

=====================
 欢迎使用 Bash 工具 
=====================

1.3 函数的参数传递:用 $n 接收

Bash 函数不支持“形参定义”,而是通过 $n 接收外部传递的参数($1 表示第1个参数,$2 表示第2个,以此类推)。若参数序号≥10,需用 ${10} 表示(避免与 $1+0 混淆)。

示例2:带参数的求和函数
#!/bin/bash
# 定义求和函数:接收2个参数,计算并打印结果
function sum() {
    # $1 接收第一个参数,$2 接收第二个参数
    local num1=$1  # local 关键字:定义局部变量(仅函数内可用)
    local num2=$2
    
    # 验证输入是否为数字(避免计算报错)
    if ! [[ "$num1" =~ ^[0-9]+$ ]] || ! [[ "$num2" =~ ^[0-9]+$ ]]; then
        echo "错误:请输入有效的正整数!"
        return 1  # 返回错误状态码(非0表示失败)
    fi
    
    local result=$((num1 + num2))
    echo "求和结果:$num1 + $num2 = $result"
}

# 调用函数:传递2个参数(10和20)
sum 10 20
# 再次调用:传递其他参数
sum 30 45

执行结果:

求和结果:10 + 20 = 30
求和结果:30 + 45 = 75

1.4 函数的返回值:用 $? 接收

Bash 函数的返回值有两种常见形式:

  1. 状态码返回:用 return 数值 返回(仅支持 0-255 的整数,0 表示成功,非0表示失败),外部通过 $? 获取;
  2. 结果返回:若需返回字符串或大于255的数字,可在函数内用 echo 输出结果,外部通过 变量=$(函数名) 捕获。
示例3:两种返回值的用法
#!/bin/bash
# 1. 状态码返回:判断数字是否为偶数
function is_even() {
    local num=$1
    if (( num % 2 == 0 )); then
        return 0  # 是偶数:返回成功状态码
    else
        return 1  # 不是偶数:返回失败状态码
    fi
}

# 2. 结果返回:计算数字的平方(返回大于255的结果)
function square() {
    local num=$1
    echo $((num * num))  # 用 echo 输出结果
}

# 测试状态码返回
is_even 12
if [ $? -eq 0 ]; then  # $? 获取上一个命令(函数)的返回码
    echo "12 是偶数"
else
    echo "12 是奇数"
fi

# 测试结果返回
result=$(square 20)  # 捕获函数的 echo 输出
echo "20 的平方是:$result"

执行结果:

12 是偶数
20 的平方是:400

二、Bash 数组:高效管理批量数据

当需要处理“一组相关数据”(如服务器列表、日志文件名、配置参数)时,数组是最优选择。Bash 支持一维数组(不支持多维),可灵活实现数据的定义、读取、遍历和修改。

2.1 数组的定义:3种常见方式

Bash 数组无需声明长度,直接赋值即可,元素间用空格分隔。

# 方式1:直接定义(最常用)
array_name=(元素1 元素2 元素3 ...)
server_list=("192.168.1.10" "192.168.1.20" "192.168.1.30")  # 示例:服务器IP数组

# 方式2:单独定义元素(指定索引)
array_name[索引]=元素
fruit[0]="apple"
fruit[1]="banana"
fruit[5]="orange"  # 索引可跳过,未赋值的索引默认为空

# 方式3:从命令输出创建数组(将命令结果按空格分割为数组元素)
file_list=($(ls /home/user/docs))  # 示例:将 /home/user/docs 下的文件存入数组

2.2 数组的读取:获取单个或所有元素

数组元素的索引从 0 开始,通过 ${数组名[索引]} 读取单个元素;通过 ${数组名[@]}${数组名[*]} 读取所有元素。

示例4:读取数组元素
#!/bin/bash
# 定义数组
fruit=("apple" "banana" "orange" "grape")

# 1. 读取指定索引的元素
echo "索引0的元素:${fruit[0]}"  # 输出 apple
echo "索引2的元素:${fruit[2]}"  # 输出 orange

# 2. 读取所有元素(两种方式)
echo "所有元素(@):${fruit[@]}"  # 输出 apple banana orange grape
echo "所有元素(*):${fruit[*]}"  # 输出 apple banana orange grape

# 3. 获取数组长度(元素个数)
echo "数组长度:${#fruit[@]}"  # 输出 4

2.3 数组的遍历:两种实用方式

遍历数组即“逐个处理元素”,常用 for 循环实现,根据场景选择不同方式。

方式1:直接遍历所有元素(推荐,无需关心索引)
#!/bin/bash
fruit=("apple" "banana" "orange" "grape")

echo "方式1:遍历所有元素"
for item in "${fruit[@]}"; do  # 用 @ 确保元素包含空格时也能正确处理
    echo "水果:$item"
done
方式2:按索引遍历(需处理索引时用,如修改元素)
#!/bin/bash
fruit=("apple" "banana" "orange" "grape")
length=${#fruit[@]}  # 获取数组长度

echo -e "\n方式2:按索引遍历"
for ((i=0; i<length; i++)); do
    echo "索引$i${fruit[$i]}"
    # 示例:修改元素(给每个水果加前缀)
    fruit[$i]="fresh_${fruit[$i]}"
done

# 输出修改后的数组
echo -e "\n修改后的数组:${fruit[@]}"

执行结果:

方式1:遍历所有元素
水果:apple
水果:banana
水果:orange
水果:grape

方式2:按索引遍历
索引0:apple
索引1:banana
索引2:orange
索引3:grape

修改后的数组:fresh_apple fresh_banana fresh_orange fresh_grape

三、Bash 正则表达式:精准过滤文本数据

正则表达式(简称“正则”)是“描述字符串模式的规则”,核心用途是检索、过滤、替换符合规则的文本。在 Bash 中,常用 grep 命令结合正则处理日志、配置文件等文本数据。

3.1 常用工具:grep 与正则搭配

grep 是 Bash 中最常用的文本过滤工具,支持基础正则和扩展正则(需加 -E 参数),以下是高频选项:

选项 功能说明
-E 启用扩展正则(无需对 {}、+、? 等元字符转义)
-c 统计匹配到的行数
-i 忽略大小写(如匹配 Appleapple
-o 只输出匹配到的内容(而非整行)
-v 反向匹配(输出不包含匹配内容的行)
-n 显示匹配行的行号
--color=auto 高亮显示匹配到的内容(终端中更易识别)

3.2 核心元字符:构建正则规则

正则的核心是“元字符”——具有特殊含义的字符,掌握这些元字符就能组合出任意规则。以下是 Bash 中常用的元字符(分基础版和进阶版):

基础元字符(必掌握)
元字符 含义 示例
^ 匹配行首(开头位置) ^root:匹配以 root 开头的行
$ 匹配行尾(结束位置) bash$:匹配以 bash 结尾的行
. 匹配除换行符(\n)外的任意单个字符 r..t:匹配 rootrest
[list] 匹配“list”中的任意一个字符 [abc]:匹配 abc
[^list] 反向匹配(匹配“list”之外的任意一个字符) [^0-9]:匹配非数字字符
* 匹配前面的子表达式“0次或多次” ro* t:匹配 rtrotroot
进阶元字符(扩展正则,需加 -E
元字符 含义 示例
{n} 精确匹配“前面的子表达式”n次 o{2}:匹配 oo(如 food 中的 oo
{n,} 至少匹配“前面的子表达式”n次 o{2,}:匹配 ooooo
{n,m} 匹配“前面的子表达式”n到m次(n≤m) o{1,2}:匹配 ooo
+ 匹配“前面的子表达式”1次或多次(等价于 {1,} o+:匹配 ooo
? 匹配“前面的子表达式”0次或1次(等价于 {0,1} o?:匹配空或 o

3.3 实战示例:用正则解决实际问题

以下示例基于 /etc/passwd 文件(Linux 系统用户配置文件),演示正则的实际用法。

示例5:匹配以 root 开头的行(行首匹配 ^
# 匹配以 root 开头的行,并显示行号
grep -n "^root" /etc/passwd

结果(类似):

1:root:x:0:0:root:/root:/bin/bash
示例6:匹配以 bash 结尾的行(行尾匹配 $
# 匹配以 bash 结尾的行,高亮显示
grep --color=auto "bash$" /etc/passwd

结果(类似):

root:x:0:0:root:/root:/bin/bash
user:x:1000:1000:user:/home/user:/bin/bash
示例7:匹配包含 3 个数字的行(用 [0-9]{3}
# 启用扩展正则(-E),匹配包含 3 个连续数字的行,统计行数(-c)
grep -Ec "[0-9]{3}" /etc/passwd

结果(类似):

5  # 表示有5行包含3个连续数字
示例8:反向匹配(排除以 # 开头的注释行)
# 查看 /etc/ssh/sshd_config,排除注释行(-v)和空行(^$)
grep -vE "^#|^$" /etc/ssh/sshd_config

结果:仅显示配置文件中的有效配置行(无注释、无空行)。

四、总结:三大特性的核心应用场景

特性 核心价值 典型应用场景
自定义函数 代码复用、模块化 封装重复逻辑(如日志打印、参数验证)、构建脚本框架
数组 批量管理相关数据 存储服务器列表、文件列表、配置参数,实现批量操作(如批量 ping 服务器)
正则表达式 精准过滤、检索文本 分析日志(如提取错误信息)、处理配置文件(如筛选有效配置)、验证输入格式

掌握这三大特性后,你可以写出更简洁、高效、易维护的 Bash 脚本。建议结合实际需求多练手(如写一个“批量检查服务器存活状态”的脚本,用数组存服务器IP,用函数封装 ping 逻辑,用正则过滤 ping 结果),逐步提升 Bash 实战能力。


网站公告

今日签到

点亮在社区的每一天
去签到