1. 数组的声明与初始化
索引数组(数字下标,从 0 开始)
# 空数组 arr=() # 直接赋值 arr=(value1 "value 2" value3) # 分步赋值 arr[0]="first" arr[1]="second" # 从命令输出或通配符构造 arr=( *.txt ) # 当前目录所有 .txt 文件 arr=( $(ls) ) # 不推荐(遇空格会拆分) # 安全方法:用 mapfile/readarray mapfile -t arr < <(ls)
关联数组(字符串下标,类似字典)
declare -A dict dict=([key1]="val1" [key2]="val2") dict["key3"]="val3"
2. 访问元素
单个元素
echo "${arr[0]}" # 第一个元素
echo "${arr[-1]}" # 最后一个元素(Bash 4.2+)
所有元素
echo "${arr[@]}" # 每个元素作为独立的"词"(推荐)
echo "${arr[*]}" # 所有元素合并成一个字符串(用 IFS 第一个字符连接)
索引列表(键)
echo "${!arr[@]}" # 索引数组得到 0 1 2 …,关联数组得到所有键
长度
echo "${#arr[@]}" # 元素个数
切片
echo "${arr[@]:start:count}" # 从 start 开始取 count 个
# 例如从第 2 个元素开始,取 3 个:
"${arr[@]:1:3}"
3. 增加元素
尾部追加(最常用)
arr+=("new_item")
arr+=("item1" "item2") # 一次追加多个
指定索引添加(覆盖或插入)
arr[5]="fifth" # 如果索引 5 不存在会创建,存在则覆盖
数组合并
arr1=(a b)
arr2=(c d)
# 将 arr2 全部追加到 arr1
arr1+=("${arr2[@]}")
开头插入
arr=( "new_first" "${arr[@]}" )
4. 删除元素
按索引删除
unset 'arr[2]' # 删除索引 2(变成稀疏数组,索引不再连续)
删除整个数组
unset arr
按值删除(删除第一个匹配)
# 删除值为 "target" 的第一个元素(索引数组)
for i in "${!arr[@]}"; do
if [[ "${arr[$i]}" == "target" ]]; then
unset 'arr[$i]'
break
fi
done
按值删除所有匹配
# 用临时数组重建
target="remove_me"
new_arr=()
for val in "${arr[@]}"; do
[[ "$val" != "$target" ]] && new_arr+=("$val")
done
arr=("${new_arr[@]}")
重新索引(消除稀疏)
arr=("${arr[@]}") # 重新连续索引,从 0 开始
5. 修改元素
直接重写索引
arr[0]="new_value"
批量替换子串(不改变原数组,需展开时处理)
# 将所有元素中的 "old" 替换为 "new",生成新数组
new_arr=("${arr[@]//old/new}")
修改特定值
同删除的方法,遍历找到后重新赋值 arr[$i]="new"。
6. 查找
判断元素是否存在(精确匹配)
方法一:循环
exists=0
for item in "${arr[@]}"; do
[[ "$item" == "$target" ]] && { exists=1; break; }
done
方法二:用关联数组做索引(适合频繁查找)
declare -A lookup
for elem in "${arr[@]}"; do lookup["$elem"]=1; done
# 然后判断:
[[ -n "${lookup[$target]}" ]] && echo "存在"
方法三:转成字符串用 grep(元素不能含换行)
if printf '%s\n' "${arr[@]}" | grep -qxF "$target"; then
echo "存在"
fi
-
-F:固定字符串(不用正则) -
-x:整行匹配 -
-q:静默
根据值获取索引
for i in "${!arr[@]}"; do
[[ "${arr[$i]}" == "$target" ]] && echo "索引:$i"
done
7. 排序
Bash 本身没有单行排序函数,需借助 sort 命令。
# 生成排序后的数组
sorted=()
while IFS= read -r line; do
sorted+=("$line")
done < <(printf '%s\n' "${arr[@]}" | sort)
# 数字排序用 sort -n,逆序用 sort -r
保留原数组并按一定规则排序(如按文件大小、时间)
# 按文件修改时间排序(需要文件名)
sorted_files=()
while IFS= read -r -d '' file; do
sorted_files+=("$file")
done < <(find . -maxdepth 1 -type f -printf '%T@ %p\0' | sort -rnz | cut -d' ' -f2-)
8. 去重
使用 sort -u
uniq_arr=()
while IFS= read -r line; do
uniq_arr+=("$line")
done < <(printf '%s\n' "${arr[@]}" | sort -u)
缺点:会改变原有顺序。
保持原有顺序去重(用关联数组记录)
declare -A seen
uniq_arr=()
for item in "${arr[@]}"; do
if [[ -z "${seen[$item]}" ]]; then
uniq_arr+=("$item")
seen[$item]=1
fi
done
9. 复制数组
# 索引数组复制
copy=( "${arr[@]}" )
# 关联数组复制
declare -A dict2
for key in "${!dict[@]}"; do
dict2[$key]="${dict[$key]}"
done
# 或一次性复制(Bash 4.3+ 支持关联数组的 @ 展开)
declare -A dict2=()
for key in "${!dict[@]}"; do dict2[$key]="${dict[$key]}"; done
单纯复制索引数组用 copy=("${arr[@]}") 就够了。
10. 数组与字符串互转
数组 → 字符串(用自定义分隔符)
str=$(IFS='|'; echo "${arr[*]}") # 用 | 连接
或通过 printf:
str=$(printf '%s|' "${arr[@]}") # 尾部会多一个 |
str=${str%|} # 去掉最后的 |
字符串 → 数组(按分隔符拆分)
IFS='|' read -ra arr <<< "$str"
注意:元素中不能包含分隔符,且 <<< 会追加换行,但 read -ra 可正常处理。
11. 循环遍历
# 遍历值
for item in "${arr[@]}"; do
echo "$item"
done
# 遍历索引和值
for i in "${!arr[@]}"; do
echo "索引 $i: ${arr[$i]}"
done
# C 风格循环(需要连续索引)
for ((i=0; i<${#arr[@]}; i++)); do
echo "${arr[$i]}"
done
推荐使用 for item in "${arr[@]}",最安全。
12. 关联数组常用操作
declare -A dict
# 增/改
dict[key]="value"
# 查
echo "${dict[key]}"
# 删
unset 'dict[key]'
# 遍历所有键值对
for key in "${!dict[@]}"; do
echo "$key => ${dict[$key]}"
done
# 检查键是否存在
[[ -v dict[key] ]] && echo "存在" # Bash 4.2+ 的 -v 测试
# 或
[[ -n "${dict[key]+exists}" ]] && echo "存在"
13. 常见陷阱与建议
-
始终用双引号包裹数组展开:
"${arr[@]}"保证元素不会因空格或通配符被再次拆分或 glob。 -
稀疏数组:
unset后索引会留下空洞,需要连续索引时用arr=("${arr[@]}")重建。 -
空数组判断:
[[ ${#arr[@]} -eq 0 ]]。 -
关联数组不能直接整体复制,必须通过键循环。
-
${arr[*]}受 IFS 影响,"${arr[*]}"会用 IFS 的第一个字符连接所有元素;而"${arr[@]}"是独立元素。
© 著作权归作者所有
下一篇: 群晖 synology
文章评论(0)