shell脚本之函数详细解释及运用,数组的定义及运用

什么是函数

通俗地讲,所谓函数就是将一组功能相对独立的代码集中起来,形成一个代码块,这个代码可
以完成某个具体的功能。从上面的定义可以看出,Shell中的函数的概念与其他语言的函数的
概念并没有太大的区别。从本质上讲,函数是一个函数名到某个代码块的映射。也就是说,用
户在定义了函数之后,就可以通过函数名来调用其所对应的一组代码。
使用shell函数优势
1、把相同的程序段定义为函数,可以减少整个程序段代码量,提升开发效率。
2、增加程序段可读性、易读性,提升管理效率。
3、可以实现程序功能模块化,使得程序具备通用性(可移植性)。

在 Shell 脚本中,函数是组织和复用代码的核心机制。下面从基础到高级全面解析 Shell 函数的用法:

一、基础语法

1. 定义函数的三种方式
标准语法
function 函数名() {
    命令序列
    return 返回值  # 可选,默认返回最后命令的退出状态(0-255)
}

简化写法1: 没有参数
function 函数名 {
指令
return
}
 

简化写法2:  省略function关键字
函数名() {
指令
return
}
2. 调用函数
函数名 参数1 参数2 ...  # 直接写函数名,无需括号

二、函数参数与变量

1. 传递参数
  • 在函数内部,通过 $1$2, ... 访问参数
  • $0 仍表示脚本本身
  • $# 表示参数数量
  • $@ 或 $* 表示所有参数
示例:计算两数之和
sum() {
    local result=$(( $1 + $2 ))  # local 声明局部变量
    echo $result
}

# 调用函数
result=$(sum 10 20)
echo "结果: $result"  # 输出: 结果: 30
2. 局部变量
  • 使用 local 关键字声明局部变量(仅在函数内可见)
  • 若未声明,则默认为全局变量
 
global_var=10

myfunc() {
    local local_var=20
    global_var=100  # 修改全局变量
    echo "函数内: local_var=$local_var, global_var=$global_var"
}

myfunc
echo "函数外: global_var=$global_var"  # 输出: 100
echo "函数外: local_var=$local_var"    # 输出: 空(变量不存在)

三、返回值与退出状态

1. return 语句
  • 返回一个整数(0-255)作为函数的退出状态
  • 常用于表示成功(0)或失败(非 0)
  • 使用 $? 获取返回值
 
check_file() {
    if [ -f "$1" ]; then
        return 0  # 文件存在,返回成功
    else
        return 1  # 文件不存在,返回失败
    fi
}

check_file "/etc/passwd"
echo "退出状态: $?"  # 输出: 0

check_file "/nonexistent"
echo "退出状态: $?"  # 输出: 1
2. 通过 echo 返回值
  • 函数可通过 echo 输出结果,外部使用 $(函数名) 捕获
 
get_username() {
    echo "$(id -un)"  # 返回当前用户名
}

user=$(get_username)
echo "当前用户: $user"  # 输出: 当前用户: root

四、函数的作用域

1. 全局变量
  • 函数可直接访问和修改全局变量
 
count=0

increment() {
    ((count++))  # 直接修改全局变量
}

increment
echo "计数: $count"  # 输出: 计数: 1
2. 局部变量
  • 使用 local 声明局部变量,避免污染全局环境
 
total=0

calculate() {
    local num1=$1
    local num2=$2
    total=$((num1 + num2))  # 修改全局变量
    local result=$((num1 * num2))  # 局部变量
    echo $result
}

product=$(calculate 5 3)
echo "乘积: $product, 和: $total"  # 输出: 乘积: 15, 和: 8

五、函数的高级用法

1. 递归函数
  • 函数可调用自身(需设置终止条件)
# 计算阶乘
factorial() {
    local n=$1
    if [ $n -le 1 ]; then
        echo 1
    else
        local prev=$(factorial $((n-1)))
        echo $((n * prev))
    fi
}

echo "5的阶乘: $(factorial 5)"  # 输出: 120
2. 函数库
  • 将常用函数保存到独立文件,通过 source 引入
 
# utils.sh 函数库
#!/bin/bash

log_info() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] $1"
}

log_error() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') [ERROR] $1" >&2
}

# 在主脚本中使用
source utils.sh
log_info "开始处理数据"
log_error "文件不存在!"
3. 函数重载(有限支持)
  • Shell 不支持真正的函数重载,但可通过参数数量模拟
 
process() {
    if [ $# -eq 1 ]; then
        echo "处理单个参数: $1"
    elif [ $# -eq 2 ]; then
        echo "处理两个参数: $1 和 $2"
    else
        echo "参数数量错误"
        return 1
    fi
}

process "apple"       # 输出: 处理单个参数: apple
process "apple" "banana"  # 输出: 处理两个参数: apple 和 banana

六、常见应用场景

1. 脚本模块化
  • 将复杂逻辑拆分为多个函数,提高可读性
 
#!/bin/bash

# 初始化环境
init() {
    echo "初始化配置..."
}

# 检查依赖
check_deps() {
    command -v curl >/dev/null || { echo "需要安装 curl"; exit 1; }
}

# 主函数
main() {
    init
    check_deps
    echo "开始主任务..."
}

# 执行主函数
main
2. 错误处理
  • 封装错误处理逻辑
 
error_exit() {
    echo "错误: $1" >&2
    exit ${2:-1}  # 默认退出状态为1
}

validate_file() {
    [ -f "$1" ] || error_exit "文件不存在: $1" 2
}

validate_file "/etc/passwd"  # 正常
validate_file "/nonexistent"  # 报错并退出

详细解释:
 error_exit() 函数
error_exit() {
    echo "错误: $1" >&2             # 输出错误信息到标准错误(stderr)
    exit ${2:-1}                   # 以指定状态码退出(默认1)
}
参数:
$1:错误消息内容
$2:可选的退出状态码(默认 1)
功能:
显示错误信息并终止脚本,适用于各种检查失败的场景。


2. validate_file() 函数
validate_file() {
    [ -f "$1" ] || error_exit "文件不存在: $1" 2  # 检查文件是否存在
}

参数:
$1:要验证的文件路径
功能:
使用 [ -f "$1" ] 检查文件是否存在
若不存在,调用 error_exit 输出错误并以状态码 2 退出
exit{2:-1详解}
一、基础语法解析
1. ${parameter:-word} 结构
作用:
如果变量 parameter 存在且不为空,则返回其值;否则返回 word。
示例:
bash
# 变量存在时
name="Alice"
echo ${name:-"默认值"}  # 输出: Alice

# 变量不存在时
unset age
echo ${age:-18}  # 输出: 18



2. exit 命令
作用:
终止当前脚本或 shell 进程,并返回一个退出状态码(范围 0-255)。
约定:
0 表示成功,非零表示失败。
常见错误码:1(通用错误)、2(误用 shell 命令)、127(命令未找到)等。
二、exit ${2:-1} 的具体含义
1. 参数映射
$2:函数的第二个参数($1 是第一个,依此类推)。
示例:
bash
error_exit "文件不存在" 2  # $1="文件不存在", $2=2
error_exit "权限不足"     # $1="权限不足", $2 未提供(为空)

2. 执行逻辑
当 $2 存在时:
bash
exit ${2:-1}  # 等价于 exit $2

示例:
bash
error_exit "文件不存在" 2  # 脚本退出状态为 2

当 $2 未提供或为空时:
bash
exit ${2:-1}  # 等价于 exit 1

示例:
bash
error_exit "权限不足"  # 脚本退出状态为 1(默认值)

七、注意事项

  1. 函数必须先定义后使用
    Shell 是逐行解释执行的,调用函数前必须确保已定义。

  2. 参数传递方式
    参数通过位置传递($1$2),没有类型检查,需自行验证。

  3. 返回值限制
    return 只能返回 0-255 的整数,复杂结果需通过 echo 输出。

  4. 命名冲突
    函数名不能与内置命令或其他函数重名,建议使用前缀(如 myapp_)。

八、总结

Shell 函数是组织脚本的关键工具,通过合理使用函数可以:

 
  • 提高代码复用性
  • 使脚本结构更清晰
  • 简化错误处理
  • 实现复杂逻辑
 

掌握函数的定义、参数传递、返回值和作用域,是编写高质量 Shell 脚本的基础

数组

一、数组的基本概念

1. 定义方式

Shell 数组无需提前声明,直接赋值即可。元素通过 ** 索引(Index)** 访问,索引从 0 开始(也支持负数索引,部分 Shell 如 Bash 支持)。

2. 元素类型
  • 可以存储任意字符串(包括空格、特殊字符)。
  • 索引可以是整数或算术表达式。

二、数组的创建与赋值

1. 直接赋值
# 语法:数组名[索引]=值
array[0]="apple"
array[1]="banana"
array[2]="orange"
2. 初始化赋值

注意:元素中间以空格分隔,如果没写或者用别的符号,都只能算是一个元素

# 语法:数组名=(值1 值2 值3...)
fruits=("apple" "banana" "orange")
3. 动态赋值(通过命令结果)
files=($(ls /etc/*.conf))  # 将命令输出按空格分割后赋值给数组
4. 稀疏数组(不连续索引)
array[0]=10
array[5]=20  # 索引 1-4 为空
echo ${array[5]}  # 输出:20

三、访问数组元素

1. 获取单个元素
# 语法:${数组名[索引]}
echo ${fruits[0]}  # 输出:apple
2. 获取所有元素
# 语法:${数组名[@]} 或 ${数组名[*]}
echo ${fruits[@]}  # 输出:apple banana orange
3. 获取元素数量(长度)sh
# 语法:${#数组名[@]}
length=${#fruits[@]}
echo "元素数量:$length"  # 输出:3
4. 负数索引(Bash 支持)
array=("a" "b" "c" "d")
echo ${array[-1]}  # 输出:d(最后一个元素)
echo ${array[-2]}  # 输出:c(倒数第二个元素)

四、数组的遍历

1. 使用 for 循环遍历索引
fruits=("apple" "banana" "orange")
for i in "${!fruits[@]}"; do  # ${!数组名[@]} 获取所有索引
    echo "索引 $i:${fruits[$i]}"
done
输出结果
索引 0:apple
索引 1:banana
索引 2:orange
2. 直接遍历元素
for fruit in "${fruits[@]}"; do
    echo "水果:$fruit"
done
输出结果
水果:apple
水果:banana
水果:orange

五、数组的常用操作

1. 添加元素(追加到末尾)
# 方法1:使用未声明的索引
fruits[3]="grape"

# 方法2:使用 `+=` 运算符(Bash 支持)
fruits+=("watermelon")
2. 删除元素
# 语法:unset 数组名[索引]
unset fruits[1]  # 删除索引为1的元素(banana)
echo ${fruits[@]}  # 输出:apple orange grape watermelon
3. 修改元素
fruits[2]="pear"  # 将索引2的元素从orange改为pear
4. 切片(截取子数组)
# 语法:${数组名[@]:起始索引:长度}
sub_array=("${fruits[@]:1:2}")  # 从索引1开始,截取2个元素
echo ${sub_array[@]}  # 输出:orange pear

六、数组与字符串的转换

1. 数组转字符串(用指定分隔符连接)
delimiter=","
str="${fruits[*]// /$delimiter}"  # 将数组元素用逗号连接
echo $str  # 输出:apple,orange,grape,watermelon

这一表达式包含三个核心部分:
${fruits[*]}:获取数组的所有元素,用当前 IFS 的第一个字符连接(默认为空格)。
//:参数扩展中的全局替换操作符。
/:分隔符,左边是要替换的模式,右边是替换值。

${fruits[*]} 会将所有元素用空格连接成字符串:
"apple orange grape watermelon"

// 全局替换
语法:${变量//旧模式/新模式}
功能:将变量中所有匹配的旧模式替换为新模式。

/ /$delimiter 的替换逻辑
旧模式:空格()
新模式:变量 $delimiter 的值(即 ,)
2. 字符串转数组(按分隔符分割)
str="apple,banana,orange"
IFS=',' read -ra array <<< "$str"  # -r 禁用转义,-a 指定数组名
echo ${array[@]}  # 输出:apple banana orange


IFS=','
IFS(Internal Field Separator,内部字段分隔符)是 Shell 的环境变量,用于指定字段分隔符(默认是空格、制表符、换行符)。
作用:将IFS临时设置为逗号(,),告诉read命令用逗号作为字符串拆分的依据。

read -ra array
-r:禁用反斜杠转义功能(避免字符串中的反斜杠被特殊处理)。
-a array:将拆分后的字段赋值给数组array(若省略-a,则赋值给单个变量)。

<<< "$str"
Here 字符串(Here String):将$str的内容作为read命令的输入流(等同于通过管道传递)。

七、二维数组(模拟实现)

Shell 原生不支持二维数组,但可通过一维数组模拟:

示例:模拟二维数组存储学生成绩
# 格式:[学生索引_科目索引]=值
scores[0_0]=90  # 学生1的语文成绩
scores[0_1]=85  # 学生1的数学成绩
scores[1_0]=80  # 学生2的语文成绩
scores[1_1]=95  # 学生2的数学成绩

# 遍历所有学生的成绩
for i in 0 1; do
    for j in 0 1; do
        echo "学生$((i+1)) 科目$((j+1)):${scores[$i_$j]}"
    done
done
输出结果
学生1 科目1:90
学生1 科目2:85
学生2 科目1:80
学生2 科目2:95

八、注意事项

  1. 索引范围

    • 正索引从 0 开始,负数索引(如 -1 表示最后一个元素)仅 Bash 支持。
  2. 元素引用

    • 使用 "${数组名[@]}" 避免元素被空格分割(保持完整性)。
  3. Shell 兼容性

    • 数组功能依赖于具体 Shell,建议在脚本开头指定 #!/bin/bash
  4. 性能限制

    • Shell 数组性能较低,不适合处理大规模数据,复杂场景建议使用其他语言(如 Python)。

九、总结

操作语法示例说明
定义数组array=("a" "b" "c")初始化数组
访问元素${array[1]}获取索引 1 的元素
遍历数组for item in "${array[@]}"; do ... done遍历所有元素
添加元素array+=("d")追加元素到末尾
删除元素unset array[2]删除索引 2 的元素
获取长度${#array[@]}获取元素数量
 

Shell 数组是处理批量数据的重要工具,合理使用数组可以显著提高脚本的灵活性和可读性。在实际应用中,需注意索引边界和 Shell 兼容性,避免因语法差异导致错误。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值