shell 数组常见用法

2026年5月11日 0 条评论 10 次阅读 0 人点赞

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[@]}" 是独立元素。

Sevenfal

这个人太懒什么东西都没留下

文章评论(0)