01
终端与路径
5 节01
Shell、终端与命令的关系
入门终端负责输入与显示,Shell 解释命令。此手册以 Bash 为准;发行版和所装工具可能不同。
printf '%s\n' "$SHELL"
bash --version | head -n 1
type cd printf ls
- 观察结果
- 可看到登录 Shell 路径、Bash 版本,以及命令是内建命令还是外部程序。
- 动手练习
- 用 type 查看 grep、pwd、echo 各属于哪类命令。
- 注意
- 环境变量 SHELL 记录登录 Shell,不一定是当前正在运行的 Shell。
官方参考 02
建立安全的练习目录
入门后续文件操作都放在自己的练习目录。先确认当前位置,再进入目录,避免误操作真实项目。
mkdir -p "$HOME/shell-lab"
cd "$HOME/shell-lab"
pwd
ls -la
- 观察结果
- pwd 应显示当前用户主目录下的 shell-lab;新目录可能只有 . 和 ..。
- 动手练习
- 关闭终端再打开时,重新进入该目录并确认 pwd。
- 注意
- 不要不看 pwd 就执行覆盖或删除命令。
官方参考 03
绝对路径、相对路径与特殊目录
入门以 / 开头的是绝对路径;相对路径以当前工作目录为基准。`.` 是当前目录,`..` 是上级目录,`~` 通常展开为当前用户主目录。
cd "$HOME/shell-lab"
pwd
cd ..
pwd
cd ./shell-lab
pwd
- 观察结果
- 第二个 pwd 显示上级目录;最后回到 shell-lab。
- 动手练习
- 用 `cd -` 在最近两个工作目录间切换。
- 注意
- 脚本里的相对路径取决于运行时的工作目录,不一定是脚本所在目录。
官方参考 04
查命令帮助
入门先确认命令是否存在,再看内建帮助、简短帮助或手册页。man 的内容可能来自本机安装的软件版本。
type grep
help cd
grep --help | head -n 12
man grep
- 观察结果
- man 内可用 /pattern 搜索、n 跳到下一个匹配、q 退出。
- 动手练习
- 查找 grep 的 -n 与 -F 选项分别做什么。
- 注意
- help 主要用于 Bash 内建命令;有些外部程序不提供 --help。
官方参考 05
读取命令历史与自动补全
入门交互式 Bash 可用方向键翻历史命令,用 Tab 补全路径和命令;history 列出当前会话历史。
history | tail -n 10
printf '%s\n' "$HISTFILE
- 观察结果
- history 显示最近输入的命令;HISTFILE 通常指向历史文件。
- 动手练习
- 输入 `cd ~/she` 后按 Tab 试着补全练习目录。
- 注意
- 历史记录可能包含敏感命令;不要把口令直接写在命令参数里。
官方参考 02
文件管理
5 节06
创建文件并查看元数据
入门printf 可以稳定写入文本;ls -l 显示权限、大小和修改时间;stat 提供更详细信息。
cd "$HOME/shell-lab"
printf 'alpha\nbeta\n' > notes.txt
ls -l -- notes.txt
stat -- notes.txt
- 观察结果
- notes.txt 包含两行文本;具体时间与大小取决于内容和环境。
- 动手练习
- 再写入第三行 gamma,并用 cat 确认。
- 注意
- `>` 会截断已有文件;追加请用 `>>`。
官方参考 07
复制、移动与改名
入门cp 复制文件;mv 移动或改名。目标已存在时可能被覆盖,操作前先查看目标。
cd "$HOME/shell-lab"
cp -i -- notes.txt notes-copy.txt
mv -i -- notes-copy.txt notes-backup.txt
ls -l -- notes*.txt
- 观察结果
- 应看到 notes.txt 与 notes-backup.txt。
- 动手练习
- 新建 drafts 目录,将 notes-backup.txt 移进去。
- 注意
- `-i` 会在覆盖前询问,但脚本不应依赖交互确认。
官方参考 08
安全删除与预览
入门rm 删除后通常无法从回收站恢复。先列出目标,再用 -i 交互确认;这里仅删除练习目录中的副本。
cd "$HOME/shell-lab"
ls -l -- notes-backup.txt
rm -i -- notes-backup.txt
ls -l -- notes.txt
- 观察结果
- 确认删除后,原文件 notes.txt 仍应存在。
- 动手练习
- 尝试用 `find . -maxdepth 1 -type f -print` 预览练习文件。
- 注意
- 不要在不确定路径时复制粘贴递归删除命令;`--` 可防止以 - 开头的文件名被当作选项。
官方参考 09
通配符与隐藏文件
基础`*`、`?`、`[abc]` 由 Shell 对文件名展开。以点开头的隐藏文件通常不会被普通 `*` 匹配。
cd "$HOME/shell-lab"
printf 'one\n' > a.log
printf 'two\n' > b.log
printf 'hidden\n' > .secret
printf '%s\n' *.log
ls -a
- 观察结果
- *.log 展开为 a.log 与 b.log;ls -a 还能看到 .secret。
- 动手练习
- 比较 `printf '%s\n' *.log` 与 `printf '%s\n' '*.log'`。
- 注意
- 若没有匹配项,Bash 默认可能保留原样的 `*.log`;不要据此假设一定找到了文件。
官方参考 10
文件类型与符号链接
基础file 猜测文件类型;ln -s 创建符号链接。链接保存目标路径,目标移动后可能失效。
cd "$HOME/shell-lab"
file -- notes.txt
ln -s -- notes.txt notes-link.txt
ls -l -- notes-link.txt
readlink -- notes-link.txt
- 观察结果
- ls -l 显示 notes-link.txt 指向 notes.txt。
- 动手练习
- 把原文件临时改名,观察链接会发生什么,再改回。
- 注意
- 符号链接与文件副本不同;删除链接本身不会删除其目标。
官方参考 03
文本检索与管道
6 节11
快速查看文件
入门cat 适合短文件;head 和 tail 分别看开头与结尾;less 适合较长文件并支持搜索。
cd "$HOME/shell-lab"
cat -- notes.txt
head -n 1 -- notes.txt
tail -n 1 -- notes.txt
less -- notes.txt
- 观察结果
- less 中按 q 退出,按 / 输入关键词搜索。
- 动手练习
- 用 `nl -ba notes.txt` 显示行号。
- 注意
- 不要用 cat 一次输出巨大日志,以免淹没终端。
官方参考 12
grep 查找文本
入门grep -n 显示匹配行号;-F 把模式当普通字符串;-i 忽略大小写。
cd "$HOME/shell-lab"
printf 'ERROR disk\nINFO ready\nerror retry\n' > app.log
grep -n -i -F 'error' app.log
- 观察结果
- 应匹配第 1 行与第 3 行。
- 动手练习
- 试试 `grep -n -v -F 'INFO' app.log`,观察 -v 的作用。
- 注意
- 默认模式是正则表达式;搜索含 `.`、`[` 等普通字符时可用 -F。
官方参考 13
find 按条件找文件
基础find 按路径、名称和类型筛选。先打印结果核对,再考虑对结果执行操作。
cd "$HOME/shell-lab"
find . -maxdepth 1 -type f -name '*.log' -print
find . -maxdepth 1 -type l -print
- 观察结果
- 第一条列出练习目录中的 .log 普通文件,第二条列出符号链接。
- 动手练习
- 在子目录里新建一个 .log 文件,再去掉 -maxdepth 1 比较结果。
- 注意
- 文件名可能包含空格或换行;批量传给其他命令时用 -print0 与支持 NUL 分隔的工具。
官方参考 14
管道把命令接起来
基础`|` 将左边命令的标准输出交给右边作为标准输入。默认情况下管道状态通常取最后一个命令。
cd "$HOME/shell-lab"
grep -i -F 'error' app.log | wc -l
printf '%s\n' b a b | sort | uniq -c
- 观察结果
- 第一条统计错误行数;第二条先排序再统计重复值。
- 动手练习
- 把 `grep -i -F 'error' app.log` 接到 `head -n 1`。
- 注意
- uniq 只合并相邻的重复行;需要全局统计时通常先 sort。
官方参考 15
重定向标准输出和错误
基础`>` 覆盖标准输出,`>>` 追加;`2>` 写标准错误。顺序会影响 `2>&1` 的效果。
cd "$HOME/shell-lab"
printf 'first\n' > result.txt
printf 'second\n' >> result.txt
ls missing-file > listed.txt 2> errors.txt
cat -- result.txt errors.txt
- 观察结果
- result.txt 有两行;missing-file 的错误写进 errors.txt。
- 动手练习
- 试用 `ls notes.txt missing-file > all.txt 2>&1`,查看两类输出。
- 注意
- `>` 会覆盖原文件;重定向在命令执行前建立,目标写错仍可能造成数据丢失。
官方参考 16
sort、uniq、wc 与 cut
基础这些工具可组合处理简单文本表格;cut -d 指定分隔符,-f 选择字段。
cd "$HOME/shell-lab"
printf 'alice:dev\nbob:ops\nalice:dev\n' > people.txt
cut -d: -f2 people.txt | sort | uniq -c
wc -l -- people.txt
- 观察结果
- 部门 dev 出现 2 次,ops 出现 1 次;文件共有 3 行。
- 动手练习
- 找出 people.txt 中不重复的姓名。
- 注意
- cut 适合简单分隔文本;包含引号和转义的 CSV 需要专门解析器。
官方参考 04
权限、进程与环境
5 节17
理解读、写、执行权限
基础ls -l 的权限分为所有者、组、其他用户三组;目录的执行位表示可进入或穿越该目录。
cd "$HOME/shell-lab"
ls -ld .
ls -l -- notes.txt
chmod u+rw,go-rwx -- notes.txt
ls -l -- notes.txt
- 观察结果
- notes.txt 仅所有者保留读写权限;具体权限还受文件系统和 ACL 影响。
- 动手练习
- 用 `stat -c '%A %a %n' notes.txt` 对照符号和数字权限。
- 注意
- 不要把 chmod 777 当作通用修复;权限应按实际需要授予。
官方参考 18
查看进程与退出状态
基础ps 列出进程;命令退出状态 0 通常表示成功,非 0 表示失败或其他状态。`$?` 保存最近前台管道的状态。
ps -o pid,ppid,stat,comm -p $$
true
printf 'true: %s\n' "$?"
false
printf 'false: %s\n' "$?
- 观察结果
- 通常看到 true: 0、false: 1;进程号因环境不同。
- 动手练习
- 用 `if grep -q -F 'alpha' "$HOME/shell-lab/notes.txt"; then ...; fi` 判断是否匹配。
- 注意
- 读取 `$?` 前不要插入其他命令,否则得到的是后来命令的状态。
官方参考 19
前台、后台与作业控制
基础交互式 Bash 中,`&` 可启动后台作业,jobs 查看当前 Shell 的作业,wait 等待子进程。
sleep 2 &
jobs
wait
printf 'done\n'
- 观察结果
- 约 2 秒后打印 done;jobs 的编号和进程号会变化。
- 动手练习
- 尝试用 Ctrl+Z 暂停一个 sleep,再用 fg 恢复。
- 注意
- 关闭终端不等于可靠的长期任务管理;长期服务应使用 systemd 等进程管理机制。
官方参考 20
环境变量与 PATH
基础Shell 变量仅在当前 Shell 中存在;export 后子进程才能继承。PATH 决定命令搜索目录。
LAB_MODE=demo
export LAB_MODE
bash -c 'printf "child: %s\n" "$LAB_MODE"'
printf '%s\n' "$PATH
- 观察结果
- 子 Bash 输出 child: demo;PATH 通常由多个冒号分隔的目录构成。
- 动手练习
- 用 `command -v bash` 查找 Bash 可执行文件路径。
- 注意
- 不要把当前目录 `.` 随意放进 PATH,以免误运行同名文件。
官方参考 21
磁盘空间与目录大小
基础df 查看文件系统可用空间;du 统计目录占用;两者衡量范围不同。
df -h "$HOME"
du -sh "$HOME/shell-lab
- 观察结果
- df 给出所在文件系统容量;du 给出练习目录占用。
- 动手练习
- 在练习目录加入一个文件,再比较 du 输出。
- 注意
- du 默认可能跟实际磁盘占用与文件逻辑大小不同;稀疏文件和硬链接会影响理解。
官方参考 05
Bash 脚本
9 节22
第一个 Bash 脚本
基础脚本是按顺序执行的文本命令。shebang 指定解释器;也可明确用 bash 文件名执行。
cd "$HOME/shell-lab"
printf '#!/usr/bin/env bash\nprintf "hello shell\\n"\n' > hello.sh
bash hello.sh
bash -n hello.sh
- 观察结果
- 运行时输出 hello shell;bash -n 只检查语法,不执行脚本。
- 动手练习
- 给 hello.sh 加执行权限,再运行 `./hello.sh`。
- 注意
- 脚本没有执行权限时,可用 `bash hello.sh`;`bash -n` 不检查运行时逻辑。
官方参考 23
变量与引号
基础赋值号两侧不能加空格。双引号保留空格并允许变量展开;单引号把变量写成字面文本。
name='Linux Shell'
printf 'hello %s\n' "$name"
printf '%s\n' '$name'
printf '%s\n' "$name
- 观察结果
- 第二条打印字面文本 $name,第三条打印 Linux Shell。
- 动手练习
- 设 `file='my notes.txt'`,比较 `printf '%s\n' "$file"` 与未加引号的写法。
- 注意
- 处理路径和普通字符串时优先引用变量:`"$file"`;不要依赖词拆分。
官方参考 24
位置参数与参数检查
基础`$1` 是第一个参数,`$#` 是参数个数;`"$@"` 可保留各参数边界。
printf '#!/usr/bin/env bash\nprintf "count=%s\\n" "$#"\nprintf "arg=%s\\n" "$1"\n' > args.sh
bash args.sh hello
- 观察结果
- 输出 count=1 和 arg=hello。
- 动手练习
- 修改脚本,在没有参数时打印用法并以非零状态退出。
- 注意
- 启用 `set -u` 后,直接读取未传入的 `$1` 会报错;可用 `${1:-}` 或先检查 `$#`。
官方参考 25
条件判断与文件测试
基础`[[ ... ]]` 是 Bash 条件语法;`-f` 检查普通文件,`-d` 检查目录。if 根据命令退出状态选择分支。
file="$HOME/shell-lab/notes.txt"
if [[ -f "$file" ]]; then
printf 'found: %s\n' "$file"
else
printf 'missing\n'
fi
- 观察结果
- 若前面创建了 notes.txt,应输出 found。
- 动手练习
- 改成检查目录 `"$HOME/shell-lab"`,再试 `-d`。
- 注意
- `[[ ... ]]` 是 Bash 特性;如果脚本用 `/bin/sh` 执行,不能假定它可用。
官方参考 26
for 循环与数组
进阶for 可遍历列表;Bash 数组用括号创建,`"${items[@]}"` 保留每个元素。
items=('alpha one' 'beta two')
for item in "${items[@]}"; do
printf 'item: %s\n' "$item"
done
- 观察结果
- 输出两行,每个含空格的元素仍保持为一个整体。
- 动手练习
- 写一个循环,逐个打印练习目录下的 `*.log` 文件名。
- 注意
- 不要用 `for file in $(ls)` 处理文件名;空格、换行和通配符会使结果出错。
官方参考 27
while read 逐行读取
进阶`IFS= read -r` 保留行首尾空白和反斜杠;从文件重定向输入,避免把管道子 Shell 的变量变化当作主 Shell 状态。
cd "$HOME/shell-lab"
while IFS= read -r line; do
printf 'line: %s\n' "$line"
done < notes.txt
- 观察结果
- notes.txt 的每一行都加上 line: 前缀。
- 动手练习
- 计算文件行数,并比较 wc -l 的结果。
- 注意
- read 对没有结尾换行的最后一行会返回非零;生产脚本需考虑该边界。
官方参考 28
函数与局部变量
进阶函数封装可复用步骤;`local` 把变量作用域限制在 Bash 函数内。
greet() {
local name=${1:-friend}
printf 'hello %s\n' "$name"
}
greet shell
greet
- 观察结果
- 先输出 hello shell,再输出 hello friend。
- 动手练习
- 写一个函数接收文件名,检查文件是否存在并打印结果。
- 注意
- 函数的 return 表示退出状态,不适合返回任意字符串;字符串可用 stdout 输出。
官方参考 29
错误处理与 pipefail
进阶`set -e`、`-u`、`pipefail` 能帮助发现部分错误,但 `set -e` 在 if、&&、管道等上下文有例外,不能代替显式检查。
set -o pipefail
if grep -F 'missing-text' "$HOME/shell-lab/notes.txt" | wc -l; then
printf 'pipeline finished\n'
fi
set +o pipefail
- 观察结果
- grep 未匹配时退出 1;启用 pipefail 后,管道状态可反映该失败,但 wc 仍会输出 0。
- 动手练习
- 把 `grep -q` 用在 if 条件中,分别处理找到、没找到和读取出错。
- 注意
- 未匹配对 grep 来说不一定是异常;不要对所有非零状态机械报错。
官方参考 30
trap 清理临时资源
进阶trap 可在脚本退出时执行清理。临时文件要用 mktemp 创建,路径必须引用,避免误删其他文件。
tmp=$(mktemp)
trap 'rm -f -- "$tmp"' EXIT
printf 'temporary\n' > "$tmp"
cat -- "$tmp
- 观察结果
- 退出当前 Shell 或脚本时会清理临时文件;在交互终端试验前先理解 trap 的作用域。
- 动手练习
- 把这些命令写进单独脚本运行,检查临时文件是否消失。
- 注意
- 建议在独立脚本里演练 trap,避免覆盖交互式 Shell 已有的 EXIT 处理。
官方参考