模块 :S03 变量与参数
篇号 :S03-04 / 42
预计阅读 :40 分钟
主线:Bash
本篇目标
掌握 Bash 索引数组 的创建、读取、遍历与传参;会用 "${arr[@]}" 保留每个元素,用 ${#arr[@]} 取长度。了解 declare 的常见用法(-a 数组、-i 整数)。学完本篇,S03 模块收尾 ;下一阶段 S04 讲退出状态与布尔逻辑。
30 秒速览
- 定义:
arr=(a b c)或arr[0]=a;下标从 0 开始。 - 全部元素:
"${arr[@]}"(传参、循环首选);"${arr[*]}"合成一个参数(少用)。 - 长度:
${#arr[@]};单个元素:${arr[i]}。 - 保存脚本参数:
arr=("$@")。 declare -a显式声明数组;declare -i声明整数变量(可选)。- 关联数组
declare -A本专栏点到为止(键值表,需时用再查手册)。
正文
1. 为什么需要数组
位置参数 "$@" 适合原样传递 ;若要在脚本里增删、按下标改、反复遍历一组数据,用数组更清晰。
bash
files=("$@") # 把命令行参数拷进数组
echo "共 ${#files[@]} 个文件"
2. 创建与赋值
bash
# 一行定义
nums=(10 20 30)
# 按下标赋值
arr[0]="first"
arr[1]="second"
arr[10]="jump" # 中间未赋值的元素为空
# 从命令输出填充(命令替换)
mapfile -t lines < file.txt # 按行读入(Bash 4+)
bash
# 空数组
empty=()
带空格的元素要加引号:
bash
paths=("/tmp/my dir" "/var/log")
3. 读取元素
| 写法 | 含义 |
|---|---|
${arr[0]} |
下标 0 的元素 |
${arr[@]} |
全部元素,每个独立(展开后多个词) |
${arr[*]} |
全部元素,合成一个词(类似 $*) |
${#arr[@]} |
元素个数 |
${#arr[0]} |
下标 0 那一项的字符串长度 |
bash
tags=(web api db)
echo "${tags[1]}" # api
echo "个数: ${#tags[@]}" # 3
4. 遍历数组
bash
hosts=(db1 db2 db3)
# 推荐:按元素遍历
for h in "${hosts[@]}"; do
echo "ping $h"
done
# 按下标遍历
for (( i = 0; i < ${#hosts[@]}; i++ )); do
echo "$i -> ${hosts[i]}"
done
不要用未引号的 "${hosts[*]}" 做 for 循环来源(会合成一项再被拆开)。
5. 数组与 "$@"
bash
#!/usr/bin/env bash
set -euo pipefail
args=("$@") # 快照一份参数
process_all() {
printf ' <%s>' "$@"
echo
}
process_all "${args[@]}" # 把数组当多个参数传入函数
函数内 "$@" 来自调用时传入的 "${args[@]}",边界与脚本参数一致。
追加元素:
bash
list=(a b)
list+=("c d" e) # 追加两个元素:"c d" 与 e
6. 切片与部分元素(了解)
bash
arr=(a b c d e)
echo "${arr[@]:1:3}" # b c d(从下标 1 起取 3 个元素)
这是数组切片 展开,与字符串 ${var:1:3} 语法相似,但作用在数组上。
7. declare 常用选项
bash
declare -a indexed # 显式索引数组
declare -i n=0 # 整数变量(算术更一致)
(( n++ ))
declare -r CONST=100 # 只读(同 readonly)
declare -x VAR=1 # 导出(同 export)
bash
declare -a files
files=(report.log access.log)
日常 arr=(...) 已足够;declare -a 多用于可读性或动态属性。
关联数组(键值,下标为字符串):
bash
declare -A user_email=(
[alice]="a@example.com"
[bob]="b@example.com"
)
echo "${user_email[alice]}"
脚本里若只需「列表」,用普通索引数组即可;declare -A 需要时再查 help declare。
8. 读脚本时的数组写法
bash
CONFIGS=("${CONFIGS[@]}" "extra.conf") # 在原有配置上追加
cmd "${ARR[@]}" # 展开为多个参数
length=${#ARR[@]}
检查清单式阅读:
- 定义是否用了
()与引号保护含空格项。 - 传参、循环是否
"${arr[@]}"。 - 长度是否
${#arr[@]}而不是${#arr}(后者是下标 0 字符串的长度,易混)。
9. 对照表
| 需求 | 写法 |
|---|---|
| 定义 | arr=(a b c) |
保存 $@ |
arr=("$@") |
| 遍历 | for x in "${arr[@]}"; do |
| 传给命令/函数 | cmd "${arr[@]}" |
| 个数 | ${#arr[@]} |
| 追加 | arr+=(item) |
读脚本检查清单
- 循环、传参是否
"${arr[@]}"? - 含空格元素定义时是否加了引号?
- 取长度是否
${#arr[@]}? - 下标 10 以上是否用
${arr[10]}? - 是否把关联数组
declare -A当成普通数组用?
练习
判断题
arr=(a b c)中元素下标从 1 开始。"${arr[@]}"与"$@"一样,适合原样传给子命令。${#arr}一定等于数组元素个数。arr=("$@")可以在脚本开头保存命令行参数。
参考答案
- 错(从 0 开始)。
- 对。
- 错(
${#arr}是下标 0 项的字符串长度;个数用${#arr[@]})。 - 对。
实操题
bash
#!/usr/bin/env bash
set -euo pipefail
words=(alpha beta gamma)
echo "个数=${#words[@]}"
for w in "${words[@]}"; do
echo "|$w|"
done
words+=("delta echo")
echo "追加后=${#words[@]}"
for w in "${words[@]}"; do
echo "|$w|"
done
观察追加后是否仍为两个新元素(含空格的一项算一个)。
改错题
bash
#!/usr/bin/env bash
set -euo pipefail
items=(one "two three" four)
for x in ${items[*]}; do
echo "$x"
done
参考
bash
for x in "${items[@]}"; do
echo "$x"
done
"${items[*]}" 在 for 里会先合成再拆分,破坏 "two three" 边界。
S03 模块小结
| 篇号 | 能力 |
|---|---|
| S03-01 | 赋值、export、readonly |
| S03-02 | 参数展开、子串、替换、大小写 |
| S03-03 | $@、$#、shift |
| S03-04 | 数组与 declare |
下一篇预告
S04-01 :《退出状态 $?:命令成功与失败》--- 进入「判断与流程」阶段,先理解每条命令的退出码,再学条件表达式。