在 Shell 脚本里,数组和字符串是数据处理的基础。Bash 提供了一维数组,支持整数索引和关联数组;而字符串操作则大量依赖变量扩展和外部工具。掌握这些基本技巧,可以让你轻松处理配置解析、日志字段拆分、批量文件重命名等日常任务。
1. 普通索引数组
定义数组时,用圆括号把元素括起来,元素之间用空格分隔:
fruits=(apple banana cherry)
也可以逐个赋值:
fruits[0]=apple
fruits[1]=banana
fruits[3]=durian # 索引可以不连续
读取某个元素使用 ${数组名[索引]},读取所有元素使用 ${数组名[@]} 或 ${数组名[*]}:
echo ${fruits[1]} # banana
echo ${fruits[@]} # apple banana cherry durian
获取数组长度(元素个数):
echo ${#fruits[@]} # 4
遍历数组最常用的写法是:
for item in "${fruits[@]}"; do
echo "水果: $item"
done
注意:用双引号括起 ${fruits[@]} 可以正确处理包含空格的元素。
2. 关联数组(类似字典)
Bash 4.0 以上支持关联数组,使用字符串作为键。声明时必须用 declare -A:
declare -A user
user[name]="张三"
user[age]=25
user[city]="北京"
读取时和索引数组类似:
echo ${user[name]} # 张三
echo ${!user[@]} # 列出所有键:name age city
echo ${#user[@]} # 元素数量:3
遍历所有键值对:
for key in "${!user[@]}"; do
echo "$key = ${user[$key]}"
done
3. 字符串基本操作
字符串变量可以直接通过花括号扩展执行多种操作,无需调用外部命令,效率很高。
- 获取字符串长度:
str="hello world"
echo ${#str} # 11
- 截取子串:
echo ${str:0:5} # hello (从索引0开始取5个字符)
echo ${str:6} # world (从索引6开始到末尾)
- 删除前缀/后缀:
path="/usr/local/bin/script.sh"
echo ${path#*/} # usr/local/bin/script.sh (最短匹配删除从头到第一个/)
echo ${path##*/} # script.sh (最长匹配删除从头到最后一个/)
echo ${path%/*} # /usr/local/bin (最短匹配删除从尾到第一个/)
echo ${path%%/*} # 空字符串 (最长匹配删除从尾到最后一个/)
- 替换子串:
text="apple banana apple"
echo ${text/apple/orange} # orange banana apple (替换第一个)
echo ${text//apple/orange} # orange banana orange (替换所有)
echo ${text/#apple/pear} # pear banana apple (匹配开头替换)
echo ${text/%apple/pear} # apple banana pear (匹配结尾替换)
4. 字符串处理的实用组合
- 批量重命名文件(结合 for 循环和变量替换):
for file in *.txt; do
mv "$file" "${file/.txt/.md}"
done
将 .txt 后缀替换为 .md。
- 切割字符串:使用
read命令配合 IFS 分隔符:
line="张三:25:北京"
IFS=":" read -r name age city <<< "$line"
echo "$name 今年 $age 岁,来自 $city"
- 使用
tr或sed处理更复杂的文本:
echo "Hello World" | tr '[:upper:]' '[:lower:]' # 转小写
echo "a,b,c" | sed 's/,/\n/g' # 逗号换行
5. 数组与字符串结合的常见场景
- 将命令输出存入数组(按行分割):
mapfile -t lines < access.log # 将文件每一行作为数组元素
echo "日志共 ${#lines[@]} 行"
- 将字符串按分隔符拆分为数组:
csv="red,green,blue"
IFS="," read -ra colors <<< "$csv"
echo ${colors[1]} # green
小结
Shell 的数组可以方便地管理列表和键值对,字符串扩展则提供了轻量级的文本处理能力。在实际脚本中,尽量优先使用内建操作(如 ${var#pattern}、${var//old/new}),它们比调用 grep、awk 等外部命令速度快得多。当逻辑更复杂或数据量很大时,再结合 awk、sed 等工具共同使用。掌握这些基础,你的 Shell 脚本就已经能够覆盖大部分日常运维和自动化需求了。