精读笔记 · 第28章 Shell 数组与函数
大约 4 分钟
精读笔记 · 第28章 Shell 数组与函数
精读整理自 RHCE9 随堂讲义(原文字版 20 页)|同名视频约 17 节(第02套) 关联知识文件:
分章笔记/01-Linux/02-07-Shell编程
一、数组概念与类型
- 变量=一个名字代表一个值;数组=一个名字代表多个值。
- 普通数组:整数做下标;关联数组:字符串做下标(需先
declare -A 数组名声明)。
二、普通数组
- 定义:
array2=(tom jack alice);array3=(\cat /etc/passwd`)(每行一个元素);array6=(1 234567 "linux shell" [20]=saltstack)(可跳号);或逐个数组[下标]=值`。 - 查看:
declare -a | grep 数组名、echo ${array1[@]}(@ 与 * 都表示全部元素)。 - 访问语法(重点记忆): | 表达式 | 作用 | | --- | --- | |
${a[0]}| 访问下标 0 | |${a[@]}/${a[*]}| 所有元素 | |${#a[@]}| 元素个数 | |${!a[@]}| 所有下标/索引 | |${a[@]:1}| 从下标 1 开始 | |${a[@]:1:2}| 从 1 开始取 2 个 | i++(先取值后自增,从 0 开始)vs++i(先自增后取值,从 1 开始)决定数组下标起点。
三、关联数组(字符串索引,统计神器)
- 必须
declare -A 数组再赋值(ass[index1]=pear或ass=([k1]=v1 [k2]=v2));修改同键即覆盖(lol[down2]=ddd)。 - 访问同普通数组:
${ass[key]}、${ass[@]}、${#ass[@]}、${!ass[@]}(键列表)。
四、数组与循环配合
- while 逐行读文件成数组:
while read line; do hosts[++i]=$line; done < /etc/hosts(按行=整行一个元素)。 - for 读文件:
for line in \cat /etc/hosts\ 会按空格分词(每词一个元素)——需要按行时用IFS=$'\n'临时改分隔符(用完还原)。 - 统计性别/用户 shell 类型(关联数组经典):
declare -A sex; while read line; do type=\echo $line|awk '{print $2}'`; let sex[$type]++; done < sex.txt;同理用awk -F: '{print $NF}'` 统计 /etc/passwd 每行最后一个字段(shell 类型)数量。
五、函数
- 意义:把特定功能代码块模块化、便于复用;先定义后调用。
- 定义两种:
函数名() { ... }或function 函数名 { ... };调用:函数名 参数1 参数2。 - 传参:函数内用
$1 $2 ...(参数跟在调用名后;脚本参数要显式转传,如fun1 $1)。 - 局部变量:
local j(仅函数内有效)。 - 返回:
return(退出码进$?);要“返回值”多用 echo 输出 + 命令替换接收。 - 案例:系统工具箱(函数 show_menu + while 循环 + case 菜单);阶乘
fun1(){ factorial=1; for((i=1;i<=$1;i++)); do let factorial*=$i; done; echo ...; },多参数fun1 $1; fun1 $2。 - 数组传参:
array ${num[*]}(函数内for i in $*遍历);函数也可产出新数组outarray[j++]=$[$i+5]再echo "${outarray[*]}"。
六、循环控制内置命令
| 命令 | 作用 |
|---|---|
exit | 退出整个脚本 |
break [n] | 跳出本层(或第 n 层)循环 |
continue | 跳过本次循环剩余代码,进下一次 |
shift [n] | 位置参数左移 n 位(默认 1) |
- 嵌套循环口诀:外层循环一次,内层全部执行;
echo -n不换行、echo单独一行输出空行。 - continue 例:内层遇到 5 跳过 → 输出 A12346789…;break 例:遇到 5 结束内层 → A1234;
break 2连外层一起跳出。 - shift 例:
while [ $# -ne 0 ]; do let sum+=$1; shift; done累加所有参数;bash shift_user.sh aa bb逐个建用户。
七、命令速查表
| 场景 | 写法 |
|---|---|
| 定义数组 | a=(x y z)、a[0]=x、[20]=v 跳号 |
| 关联数组 | declare -A a 后 a[key]=v |
| 全部/个数/索引 | ${a[@]}、${#a[@]}、${!a[@]} |
| 切片 | ${a[@]:1:2} |
| 读文件成数组 | while read line; do a[++i]=\$line; done < 文件 |
| 改分隔符 | IFS=\$'\n'(用完还原) |
| 定义函数 | f() { ...; }、function f { ...; } |
| 函数传参 | f $1(内部 $1)、数组 f \${a[*]} |
| 局部变量 | local j |
| 循环控制 | break 2、continue、shift |
| 累加 | let sum+=$1、let aaa+=1 |
八、易错点
${a}是普通变量,数组必须${a[下标]}/${a[@]},漏掉[@]只取第 0 个。declare -A忘了声明关联数组会报“关联数组引用”错;声明必须在赋值前。- for 读文件默认按空格分词——要整行处理用
while read line或改 IFS。 i++与++i的下标起点不同;let sex[$type]++是计数累加写法。- 函数参数≠脚本参数:脚本
$1不会自动进函数,要显式fun1 $1传递。 - continue 跳本次、break 跳循环、exit 退脚本、shift 移参数,别混。
九、实操清单
普通数组 6 种取值练习 → 关联数组键值遍历 → while 逐行 + for 逐词读 /etc/hosts 对比 → IFS 改分隔符实验 → 统计 sex.txt 性别、/etc/passwd shell 类型 → 函数菜单工具箱、阶乘多参数、数组传参/返回 → 循环嵌套输出 A..D×1..9(continue/break/break 2 变体)→ shift 累加参数。
十、本章自测
- 普通数组与关联数组区别?2. ${a[@]}/${#a[@]}/${!a[@]} 含义?3. while read 与 for cat 读文件的差异与解决办法?4. declare -A 何时必须写?5. 函数两种定义与传参写法?6. break/continue/exit/shift 各自作用?7. i++ 与 ++i 作下标时起点?
