数组
在 Bash 中,数组是一种非常有用的数据结构,可以存储多个值
数组定义方法
- 方法一:直接赋值
arr=(value0 value1 value2 ...)
例如:
arr=(1 2 3 4 5)
- 方法二:使用索引赋值
arr=([0]=value [1]=value [2]=value ...)
例如:
arr=([0]=1 [1]=2 [2]=3)
- 方法三:从字符串创建数组
list="value0 value1 value2 ..."
arr=($list)
例如:
list="1 2 3 4"
arr2=($list)
- 方法四:逐个赋值
arr[0]="value"
arr[1]="value"
arr[2]="value"
例如:
arr3[0]="1"
arr3[1]="2"
arr3[2]="3"
数组展开
"$*"
:将所有参数作为一个整体展开。"$@"
:将每个参数分别展开。"$#"
:返回参数的数量。
示例
#!/bin/bash
arr=("a" "b" "c")
echo "使用 \"${arr[*]}\" 进行展开:"
for item in "${arr[*]}"; do
echo $item
done
echo "使用 \"${arr[@]}\" 进行展开:"
for item in "${arr[@]}"; do
echo $item
done
注意:
- 在
for
循环中,建议使用双引号来避免潜在的单词分割问题。 ${arr[*]}
和${arr[@]}
的区别在于,${arr[*]}
将数组元素作为一个整体字符串展开,而${arr[@]}
将每个元素分别展开为独立的字符串。
数组包括的数据类型
- 数值类型:整数、浮点数等。
- 字符类型:使用单引号
' '
或双引号" "
定义的字符串。
获取数组长度
使用 ${#arr[*]}
或 ${#arr[@]}
可以获取数组的长度,即数组中元素的总数。
示例:
[root@loaclhost shuzu1]# arr1=(1 2 3 4 5)
[root@loaclhost shuzu1]# echo ${#arr1[*]} # 也就是数组中总共多少个元素
5
读取某下标赋值
使用 ${arr[index]}
可以读取指定下标的数组元素,并进行赋值。
示例:
[root@loaclhost shuzu1]# arr1=(1 2 3 4 5)
[root@lo.amazonaws.com shuzu1]# echo ${arr1[0]} # 获取索引为0的元素,即第一个元素
1
[root@loaclhost shuzu1]# echo ${arr1[3]} # 获取索引为3的元素,即第四个元素
4
数组遍历
可以使用 for
循环遍历数组中的所有元素。
示例脚本 a.sh
:
#!/bin/bash
arr5=(1 2 3 4 5)
for i in "${arr5[@]}"; do
echo $i
done
运行脚本:
[root@loaclhost shuzu1]# chmod +x a.sh
[root@loaclhost shuzu1]# ./a.sh # 将数组中的元素列出来就叫数组遍历
1
2
3
4
5
注意:
- 在
for
循环中,建议使用双引号来避免潜在的单词分割问题。 ${arr[*]}
和${arr[@]}
的区别在于,${arr[*]}
将数组元素作为一个整体字符串展开,而${arr[@]}
将每个元素分别展开为独立的字符串。
数组切片
数组切片允许你获取数组中的某一段元素的值。
- 格式:
${数组名[@或*]}:起始位置(起始索引):长度
- 示例:
[root@loaclhost shuzu1]# arr1=(1 2 3 4 5)
[root@loaclhost shuzu1]# echo ${arr1[*]} # 输出整个数组
1 2 3 4 5
[root@loaclhost shuzu1]# echo ${arr1[*]:0:2} # 从索引0开始获取往后两位元素的值
1 2
[root@loaclhost shuzu1]# echo ${arr1[*]:2:2} # 从索引2开始获取往后两位元素的值
3 4
数组替换
数组替换允许你临时或永久地替换数组中的元素。
- 临时替换格式:
${数组名[@或*]/查找字符/替换字符}
- 永久替换方法:通过重新赋值实现。
- 示例:
[root@loaclhost shuzu1]# arr1=(1 2 3 4 5)
[root@loaclhost shuzu1]# echo ${arr1[*]/4/66} # 将数组中的元素4临时替换为66
1 2 3 66 5
[root@loaclhost shuzu1]# echo ${arr1[*]} # 原来的数组元素值不变
1 2 3 4 5
[root@loaclhost shuzu1]# arr1=(${arr1[*]/4/66}) # 永久替换
[root@loaclhost shuzu1]# echo ${arr1[*]} # 替换后的数组
1 2 3 66 5
删除数组
使用 unset
命令可以删除整个数组或数组中的某个元素。
- 删除整个数组:
unset arr1
- 删除数组中的某个元素:
unset arr1[索引]
- 示例:
[root@loaclhost shuzu1]# unset arr1
[root@loaclhost shuzu1]# echo ${arr1[*]} # 数组已被删除
[root@loaclhost shuzu1]# arr1=(1 2 3 4 5)
[root@loaclhost shuzu1]# echo ${arr1[*]} # 输出整个数组
1 2 3 4 5
[root@loaclhost shuzu1]# unset arr1[2] # 删除索引2对应的元素
[root@loaclhost shuzu1]# echo ${arr1[*]} # 删除后的数组
1 2 4 5
追加数组中的元素
方法一:直接指定索引
当你知道要追加的位置时,可以直接指定索引并赋值。如果索引超出了当前数组的范围,Bash 会自动扩展数组。
arr1=(1 2 3 4 5)
arr1[5]=6 # 追加索引为5的元素
echo ${arr1[*]} # 输出 1 2 3 4 5 6
方法二:使用数组长度
如果要在数组末尾追加元素,可以使用 ${#arr1[@]}
来获取数组长度,并将其作为新元素的索引。
arr1[${#arr1[@]}]=7 # 追加新元素到末尾
echo ${arr1[*]} # 输出 1 2 3 4 5 7
方法三:使用 +=
操作符
+=
操作符可以直接将新元素追加到数组末尾,这种方式更加直观和方便。
arr1=(1 2 3 4 5)
arr1+=(6 7) # 追加多个元素
echo ${arr1[*]} # 输出 1 2 3 4 5 6 7
注意,这里不需要将 ${arr1[@]}
放在双引号内,因为 +=
操作符会自动处理。
方法四(补充):使用 readarray
或 mapfile
(Bash 4.0+)
如果你的 Bash 版本支持 readarray
(或等价的 mapfile
),你还可以从文件或其他命令的输出中直接读取到数组中。但这不是直接追加元素的方法,而是另一种填充数组的方式。
# 假设有一个文件 list.txt 包含一些要追加的元素
readarray -t arr1 < list.txt # 注意,这会覆盖 arr1 的现有内容
好的,我会更详细地解释上述内容
向函数传数组参数
在 Bash 中,当你尝试将一个数组作为参数传递给函数时,函数只会接收到数组的第一个元素。这是因为 Bash 不支持将整个数组作为一个单独的参数传递。
问题展示
考虑以下脚本 b.sh
:
#!/bin/bash
test1 () {
echo "接受到的参数列表:$@"
abc2=$1
echo "新数组的值为:${abc2[*]}"
}
abc=(3 2 1 4 5)
echo "原始数组的值为:${abc[*]}"
test1 $abc
执行此脚本时,你会看到:
原始数组的值为:3 2 1 4 5
接受到的参数列表:3
新数组的值为:3
如你所见,函数 test1
只接收到了数组 abc
的第一个元素。
2. 解决方案
为了解决这个问题,你可以:
- 将数组的每个元素作为单独的参数传递给函数。
- 在函数内部,使用
$@
或$*
(加双引号)来接收这些参数,并重新组合它们为一个数组。
考虑以下脚本c.sh
:
#!/bin/bash
test2 () {
abc1=($@) # 使用 $@ 接收所有参数,并重新组合为数组
echo "新数组的值为:${abc1[*]}"
}
abc=(seq 1 10)
test2 ${abc[*]} # 将数组的值分解为单个的值并传递
执行此脚本时,你会看到:
新数组的值为:1 2 3 4 5 6 7 8 9 10
3. 从函数返回数组
你还可以从函数中返回一个数组。这通常涉及到在函数内部生成一个数组,并使用 echo
打印其元素,然后在函数外部捕获这些输出并重新组合为一个数组。
例如,考虑一个执行加法运算的函数:
#!/bin/bash
test2 () {
abc1=($@)
sum=0
for i in ${abc1[*]}
do
sum=$((sum + i))
done
echo "$sum"
}
abc=(3 2 1 4 5)
result=$(test2 ${abc[*]})
echo "加法结果为:$result"
此脚本会输出:
加法结果为:15
另一个例子是乘法运算:
#!/bin/bash
test3 () {
abc1=($@)
for ((i=0; i<${#abc1[@]}; i++))
do
abc1[i]=$((abc1[i] * 2))
done
echo "${abc1[*]}"
}
abc=(1 2 3)
result=($(test3 ${abc[*]}))
echo "乘法结果为:${result[*]}"
此脚本会输出:
乘法结果为:2 4 6
数组排序算法:冒泡排序
冒泡排序的基本思想
冒泡排序的基本思想是:每次遍历数组时,比较相邻的两个元素,如果它们的顺序不正确(例如,对于升序排序,较大的元素在较小的元素前面),则交换它们的位置。这个过程会重复进行,直到整个数组排序完成。
冒泡排序的 Bash 实现
示例:
#!/bin/bash
bubble_sort() {
local arr=("${!1}")
local len=${#arr[@]}
for ((i = 0; i < len; i++)); do
for ((j = 0; j < len - i - 1; j++)); do
if [[ ${arr[j]} -gt ${arr[j + 1]} ]]; then
# 交换元素
temp=${arr[j]}
arr[j]=${arr[j + 1]}
arr[j + 1]=$temp
fi
done
done
echo "${arr[@]}"
}
# 示例数组
numbers=(5 3 8 1 6)
echo "原始数组:${numbers[*]}"
sorted_numbers=($(bubble_sort numbers[@]))
echo "排序后的数组:${sorted_numbers[*]}"
解释
- 函数定义:
bubble_sort
函数接受一个数组引用作为参数。 - 数组解引用:使用
${!1}
解引用传递的数组引用,获取实际的数组。 - 外层循环:外层循环控制排序的轮数,总共需要进行
len - 1
轮。 - 内层循环:内层循环用于比较相邻的元素,如果顺序不正确则交换它们。
- 交换元素:使用临时变量
temp
交换两个元素的值。 - 输出结果:排序完成后,输出排序后的数组。
运行示例
$ ./bubble_sort.sh
原始数组:5 3 8 1 6
排序后的数组:1 3 5 6 8
注意事项
- 数组引用:传递数组时使用
@
而不是*
,因为@
会保留数组的每个元素作为单独的参数,而*
会将所有元素作为一个字符串传递。 - 解引用:在函数内部使用
${!1}
解引用传递的数组引用。 - 交换元素:使用临时变量
temp
进行元素交换。