Linux Shell 脚本:从零到进阶的实战笔记
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
/ select
-
if/elif/else:典型登录校验;注意在
[[ ]]
中连写&&
。 -
case:适合“多分支输入分发”;支持模式
* ? [ ] |
。case "$addr" in"changsha") echo "去长沙旅游"; echo "去长沙吃臭豆腐";;"beijing") echo "去北京旅游"; echo "去北京故宫看三宫六院";;*) echo "回家休息";; esac
(语法与示例都在文档中)
-
select:交互式菜单(文档“目录”中列出),在需要“让用户选项”的场景很有用。
5. 循环:for
(三种姿势)/ while
/ until
5.1 for
三连
-
列表式
for item in $(ls); doecho "找到文件:$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); dotouch "$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 )); doecho "第$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 1elselocal 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/*; doif [ -d $fd ]; thenecho "$fd 是目录"list "$fd"elseecho "$fd 是文件"fidone } 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; doread -p "请输入姓名: " nameread -p "请输入电话: " telecho $name $tel >> users.txtread -p "输入 exit 退出: " answerif [[ $answer == "exit" ]]; then break; fi done Exit 0 # ← 文档此处大小写写成 Exit,会报 “command not found”
(逻辑 OK,但
Exit
应为exit
,且删文件不必-r
。示例原文如此,记录以便你对照) -
读文件(逐行解析成“数组”)
fileName="users.txt" cat $fileName | while read item; doecho "读到数据:$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); domkdir -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 的常见坑。配合上面的练习,你可以很快搭起稳妥的脚本骨架。