Linux Shell 脚本:从零到进阶的实战笔记1. Shell 脚本是什么?怎么跑?Shell 脚本:纯文本程序,用 Shell 语法完成任务;相比用 C/C++ 去写小工具,“脚本化”的门槛低、迭代快,适合运维与批处理。
两种执行方式
bash script.sh(显式用哪个解释器)chmod +x script.sh && ./script.sh(依赖首行 Shebang)Shebang 常写 #!/bin/bash 或 #!/bin/sh;如果用“方式 1”执行,首行 Shebang 不生效(因为你已经指定了解释器)。
“Hello world” 极简示例:
#!/bin/bash
echo "hello world"
(文档也在示例中强调 echo 输出后自带换行。)
2. 变量与“弱类型”:赋值、拼接、位置参数赋值与替换
赋值:name=value,等号两边不能有空白。使用:$name 才是“变量的值”,这叫变量替换。类型
Shell 变量无需声明类型,默认按字符串理解,“用法决定类型”。位置参数
$0 脚本名;$1…$9 参数 1~9;${10} 表示第 10 个;$@/$* 表示“全部参数”(展开规则略有差异)。→(目录大纲中有“位置参数/退出状态”专节)3. 常见运算与判断:算术/字符串/文件测试/逻辑算术(整数)
(( )) 或 $(( )) 做加减乘除与自增自减;支持 let(文档示例里也出现了 let)。比较与逻辑
字符串判空:-z, -n(配 [ ] 时需加引号:[ -z "$name" ])。逻辑与/或:[[ ... && ... ]]、[[ ... || ... ]](在 [[ ]] 下写更安全)。文件测试(存在/类型/权限/特殊位)
常见:-e/-f/-d/-r/-w/-x;特殊位:SGID/SUID 的语义与场景也有解释(目录 SGID 让新文件自动继承组;SUID 以文件所有者权限执行)。
示例:当前脚本是否可执行
if [ -x "$0" ]; then echo "有执行权限"; else echo "没有执行权限"; fi
(文档给了完整示例)
4. 分支控制:if / case / selectif/elif/else:典型登录校验;注意在 [[ ]] 中连写 &&。
case:适合“多分支输入分发”;支持模式 * ? [ ] |。
case "$addr" in
"changsha") echo "去长沙旅游"; echo "去长沙吃臭豆腐";;
"beijing") echo "去北京旅游"; echo "去北京故宫看三宫六院";;
*) echo "回家休息";;
esac
(语法与示例都在文档中)
select:交互式菜单(文档“目录”中列出),在需要“让用户选项”的场景很有用。
5. 循环:for(三种姿势)/ while / until5.1 for 三连列表式
for item in $(ls); do
echo "找到文件:$item"
done
(演示“命令输出作为列表”)
配合 seq
s=0
for i in $(seq 1 10); do ((s+=i)); done
echo "$s"
(seq 还能 -w 补零、-s 指定分隔符、-f 使用 printf 风格格式)批量创建带序号文件
for item in $(seq -f "file_%03g" 1 10); do
touch "$item"
done
(%03g → 左侧补零三位)
C 风格
s=0
for ((i=1; i<=100; i++)); do ((s+=i)); done
echo "$s"
(整数循环写法更直观)
5.2 while基础写法(文档给了 let 与 (( )) 两个版本):
s=0; i=1
while (( i <= 100 )); do
(( s += i )); (( i++ ))
done
echo "$s"
(break/continue 在 while/for 都可用)
5.3 until模板:until <条件>; do ...; done
语义与 while 相反:当条件为真时退出,否则继续循环。
i=1
until (( i >= 10 )); do
echo "第$i 次循环"
((i++))
done
(文档特别标注了“条件为真时退出”这一点,容易误解)
6. 函数:两种定义、参数、返回值(return vs echo)定义两种写法:function f {} 与 f() {} 都可。示例里让用户输入数字并回显平方:echo $((num*num))。
参数:$# 参数个数;$1/$2 访问各参数。示例演示“传入 Rock Tony,打印参数个数与每个值”。
返回值
return 仅 0–255(超过会溢出):
myFunc1(){ ((ret=$1+$2)); return $ret; }
myFunc1 255 2; echo $?
(第二次会溢出)
echo 可返回任意文本:
myFunc2(){ ((ret=$1+$2)); echo "$ret"; }
ret=$(myFunc2 255 2); echo "$ret"
(用命令替换接收)
**多次 echo 的“只取最后一行”**技巧:
myFunc(){ echo "China"; echo "ChangSha"; }
ret=$(myFunc | tail -1) # ret=ChangSha
(文档明确给了“tail -1 取最后一行”的范式)
7. 递归:阶乘 & 目录 DFS阶乘(基线 + 递归步)
fact() {
if (( $1 <= 1 )); then echo 1
else
local tmp=$(( $1 - 1 ))
local result=$(fact "$tmp")
echo $(( $1 * result ))
fi
}
read -p "请输入一个正整数:" num
result=$(fact "$num")
echo "$num 的阶乘为: $result"
(用 local 隔离变量、用命令替换把子结果带回)
递归遍历目录(DFS)
list() {
for fd in $1/*; do
if [ -d $fd ]; then
echo "$fd 是目录"
list "$fd"
else
echo "$fd 是文件"
fi
done
}
list "/etc"
(思路清晰:遇目录先打印再“下钻”;生产中可加引号、防空匹配、处理符号链接)
8. 数组:一维、从 0 开始、遍历与长度语法:arr=(v1 v2 ... vn);成员用空格分隔,不用逗号;索引从 0 开始。
示例
data=(1 3 5 "长沙")
echo "${data[0]}" # 第 1 个
echo "元素个数: ${#data[@]}" # 长度
for item in "${data[@]}"; do echo "$item"; done
(遍历必须用 "${arr[@]}",这点文档也点名强调)
文档说明:Bash 原生只支持一维数组(关联数组属另一个概念,文档未展开)。
9. 文件 I/O:逐行写入与读取写文件(交互式追加)
rm -rf users.txt
while true; do
read -p "请输入姓名: " name
read -p "请输入电话: " tel
echo $name $tel >> users.txt
read -p "输入 exit 退出: " answer
if [[ $answer == "exit" ]]; then break; fi
done
Exit 0 # ← 文档此处大小写写成 Exit,会报 “command not found”
(逻辑 OK,但 Exit 应为 exit,且删文件不必 -r。示例原文如此,记录以便你对照)
读文件(逐行解析成“数组”)
fileName="users.txt"
cat $fileName | while read item; do
echo "读到数据:$item"
array=($item)
echo "一共有${#array[@]}个数据: "
echo "第 1 个:${array[0]} 第 2 个: ${array[1]}"
Done
Exit 0
说明:这段用于演示“按空白切分”后取前两个字段,但示例里把 done/exit 写成 Done/Exit(大小写错误);实际应为小写。
10. 常见易错点(结合文档示例逐条“排坑”)大小写敏感:done/exit 必须小写;示例中 Done/Exit 会报错。until 的语义:条件为真就退出;若一开始条件已真,循环体一次都不会进。数组遍历:应使用 "${arr[@]}",否则含空格的元素会被拆裂。文件测试加引号:[ -z "$name" ],防止空值导致语法歧义。逻辑与/或:在 [[ ]] 中连写 &&/|| 更稳;文档示例就是如此。return vs echo:return 只能 0–255;要返回任意值,用 echo + 命令替换,并可 | tail -1 取最后一行。for + seq 的格式化:记住 %03g 之类的 -f 用法(补零、定宽)很实用。11. 实战练习清单(边学边做)练习 A:批量建 100 个目录并写入占位文件
for d in $(seq -f "day_%03g" 1 100); do
mkdir -p "$d" && echo "hi" > "$d/readme.txt"
done
```(把 `seq -f` 用熟):contentReference[oaicite:47]{index=47}
练习 B:用 case 做“环境切换”脚本(dev/test/prod),复用上面的 case 模式匹配范式。
练习 C:把“阶乘递归”改成迭代版本(比较性能与可读性)。
练习 D:把“读 users.txt”改成制表符分隔**,并对字段数做校验(“不满两列就报错跳过”),巩固数组使用。
12. 速查表(贴墙上)运行方式:bash script.sh / chmod +x && ./script.sh;Shebang 指定解释器。变量:a=1、echo "$a";拼接:a="${a} China"。判断:[[ $x == $y && -n "$z" ]];文件测试:-e/-f/-d/-r/-w/-x。循环:for/while/until;break/continue 都支持;until 条件真时退出。函数:f(){ ... };参数 $#/$1…;return 0–255;val=$(f) 接收 echo 值。数组:arr=(a b "长沙");${#arr[@]} 长度;"${arr[@]}" 遍历。文件 I/O:写 >>;读 while read;示例中 Done/Exit 大小写需改成小写才可运行。结语这份笔记基本把 PDF 里的知识点“结构化 + 实战化”整理了出来:入门要素(变量/判断/循环)、函数与返回值的取舍、递归与 DFS 思路、数组与 I/O 的常见坑。配合上面的练习,你可以很快搭起稳妥的脚本骨架。