元数据卡
- 前置知识:第1章(计算机到底是什么)
- 预计时间:40 分钟
- 核心难度:
- 阅读模式:🍃 轻松漫游
- 完成标志:能够在终端中完成文件创建/移动/删除;理解 PATH 和环境变量;会用通配符和管道组合命令
你在哪
你还在出发前的工坊里。上一章的课桌摊着计算机的拆解图——你刚搞懂了电脑肚子里有什么,但现在你得学会怎么跟它说话。
但你看完全部作业的时候,发现工坊主人不见了。
桌上留了一张地图——一张泛黄的羊皮纸,画着一个奇怪的符号:$。
地图底下压着一张字条:
"我在工坊地下的机房。下来找我。但你先要学会怎么跟电脑说话——我不觉得你每一次都想点鼠标。走左边的门,打开终端。我们在楼下见。"
你看了看左边。那里有一扇门,门上写着:TERMINAL。
你的任务
这章之后,你不会再害怕那个黑底白字的窗口。你会知道如何用它——如何浏览文件、创建项目目录、杀死坏掉的程序、运行你的第一行代码。
终端不是"旧时代的遗物"——它是开发者最常用的工具。熟练用终端的人,操作效率是纯鼠标的三倍。不是因为你打字更快,是因为你可以把一串操作写成一个命令、组合起来、甚至写成脚本跑一整夜。
今天的目标:学会用键盘和电脑说话。
本章分层
- 必读:
pwd、cd、ls、mkdir、touch、cat、rm,以及简单的输出重定向(>和>>)- 选读:
PATH环境变量、通配符(*、?)、管道(|)组合命令- 深水区:正则表达式、Shell 配置文件(
.bashrc/.zshrc)、自定义环境变量本章不会要求你掌握
- 正则表达式的完整语法——知道
grep可以按模式搜索就够了- Shell 脚本编程(for 循环、if 条件、函数)
- 系统配置文件恢复技巧
遭遇战 → 获得技能
第一回合:第一次对话
"你会跟电脑说话吗?"
工坊主人站在左边那扇标着"TERMINAL"的门前,抱着胳膊。"不是用鼠标点,是像跟一个老工匠打招呼那样——说它的语言。"
他敲了敲门。门里传来一个低沉的声音:"谁?"
"你试试。"他用下巴指了指门把手。"推开它,说句话。"
你推开 Terminal 的门,里面是一片漆黑。只有一个小小的闪烁的光标——一条竖线,在一行文字末尾一明一灭。
屏幕上只有一行:
steven@workshop:~$这个 $ 就是你的提示符(prompt)。它在跟你说话:
"我准备好了,請告诉我下一步做什么。"
你可以看到提示符的几个部分:
steven— 你现在是谁(当前登录的用户名)workshop— 你在哪台机器上(主机名)~— 你当前在哪个目录(~是家目录的缩写)$— 这是一个普通用户的 shell
试着敲几个字母。先输入 whoami,然后按回车。
# 告诉你是谁
whoami
# 预期输出:
# steven语言:Shell (Bash/Zsh) 如何运行:打开终端,输入命令后按回车 预期输出:你的用户名 你试试:hostname 看机器名,date 看日期时间,uptime 看电脑开机多久了
你输入的 whoami 是命令,按的回车是执行。终端把你的命令交给 Shell——Shell 是一个程序,它解析你的输入,找到对应的可执行文件(还记得第一章的程序吗?),执行它,然后把输出显示在屏幕上。
你可能还没意识到,你已经在跟电脑对话了。
第二回合:识路——目录和路径
你成功对着终端说了一句话——"whoami",它回了你的名字。但你忽然意识到一件事:在这片黑暗中,你连自己站在哪都不知道。
就像刚进一座巨大的地下工坊,你听到回声,但看不到脚下的路。你摸着墙壁走了两步——"我这是在工坊的哪个角落?"
"你在哪?"——这是终端里永远要知道的第一个问题。
在文件系统里,你永远有一个"当前目录"。提示符的 ~ 告诉你,你现在在家目录里。
# 打印当前工作目录
pwd
# 预期输出:
# /home/steven语言:Shell (Bash/Zsh) 如何运行:输入 pwd,回车 预期输出:当前所在目录的完整路径 你试试:先 cd /tmp,再 pwd,看看路径怎么变
pwd= Print Working Directory(打印工作目录)
文件系统是一棵树——根是 /,然后分叉出 home, usr, etc, var……每个分叉又可以继续分。/home/steven 就是你在树上的位置。
你可以在这棵树上移动:
# 切换到 /tmp 目录
cd /tmp
# 查看 /tmp 里有什么
ls
# 回到上一级目录
cd ..
# 回到刚才的目录(还记得 /tmp 吗?)
cd -语言:Shell (Bash/Zsh) 如何运行:cd <路径>,ls 不加参数 预期输出:cd 无输出,ls 显示当前目录下的文件和子目录 你试试:用 cd 在各个目录之间跳来跳去,每跳一次用 pwd 确认位置
路径有两种:
- 绝对路径:从根
/开始写,比如/home/steven/projects - 相对路径:从当前目录开始写,比如
../steven/projects(..表示上一级,.表示当前目录)
按 Tab 键可以自动补全路径——你打出前几个字符,按下 Tab,Shell 会帮你补全。如果有多个可能,再按一次 Tab 会列出所有选项。这个习惯一定要养成,它省的时间超过你想象。
第三回合:触摸这个世界——文件操作
"知道你站在哪,很好。"工坊主人的声音从某个角落传来。"但光站着有什么用?你总得动手吧。"
你看了看四周——空荡荡的黑底白字世界,什么都没有。"可是这里什么都没有啊……"
"那就创造点什么。"他的声音带着笑意。"工坊的地上不是有材料吗?捡一块符文石板,刻上你的名字。"
现在你知道自己在哪了。该动手了。
你在工坊的一角发现一个空白的符文石板——它的名字叫 first-try.txt。
# 创建一个空文件
touch first-try.txt
# 确认它已经存在
ls -l first-try.txt语言:Shell (Bash/Zsh) 如何运行:先 cd ~ 到自己目录,然后逐条输入 预期输出:ls -l 显示文件大小、权限、修改时间 变成什么了:当前目录下多了一个 first-try.txt 文件,大小 0 字节
touch的字面意思是"触摸"。如果文件不存在——创建它。如果文件已存在——更新它的修改时间。
现在往里面写点东西:
# 用 echo 把文字写入文件
echo "Hello, Workshop!" > first-try.txt
# 查看文件内容
cat first-try.txt
# 预期输出:
# Hello, Workshop!语言:Shell (Bash/Zsh) 如何运行:先确保 first-try.txt 存在,然后运行这两条 预期输出:终端打印出 "Hello, Workshop!" 你试试:用 echo "另一行文字" >> first-try.txt(两个 >)追加内容,而不是覆盖
这里有一个微妙的符号——>。
> 叫重定向。你不是在"告诉 Shell 把输出写到文件"——你是说:"把 echo 命令的输出,从默认的屏幕显示,重定向到文件里去。"
# 把日期写入文件
date > timestamp.txt
# 把进程列表写入文件
ps aux > processes.txt
# 看看写了什么
cat processes.txt | head -5如果文件已存在,> 会覆盖它。>> 会追加到文件末尾,不会覆盖。
第四回合:目录操作——建营地
你在终端里戳了几个文件出来,就像在空地上放下了几块石板。但你很快发现一个问题——石板乱丢在地上,用不了多久就找不到了。
"看看你周围。"工坊主人的声音从头顶传来。"我的工坊有分区的:锻造区在左边,材料区在右边,图纸挂在墙上。你的终端里呢?"
你低头看着那堆文件——它们混在一起,像把螺丝刀和锅铲扔进同一个抽屉。
"你需要建几个架子。"
你决定为这次冒险建一个专门的目录——"我的项目"。
# 创建目录
mkdir my-project
# 进入目录
cd my-project
# 在里面再建一个子目录
mkdir src docs
# 看看结构
ls -l
# 预期输出:
# drwxr-xr-x 2 steven steven 4096 Jun 23 16:00 docs
# drwxr-xr-x 2 steven steven 4096 Jun 23 16:00 src语言:Shell (Bash/Zsh) 如何运行:逐条输入 预期输出:ls -l 显示两个子目录 你试试:mkdir -p project/{src,test,docs} 一次性创建嵌套目录结构
删除也简单,但要小心——删除不经过回收站。
# 删除文件
rm first-try.txt
# 删除空目录
rmdir docs
# 删除目录及其所有内容(小心使用)
rm -rf src语言:Shell (Bash/Zsh) 如何运行:确认文件/目录存在后运行 注意:rm -rf 很危险,它会递归删除所有内容且不确认。永远在运行前检查你要删除的路径。
# 安全做法:先 ls 查看要删的路径
ls src/
# 确认后:
rm -rf src/
# 或者加 -i 交互模式,逐个确认
rm -ri src/进阶: 以下三个回合(PATH、通配符、管道)不是终端生存必需。读完主线后可以回来探索,但第一次读可以先跳过,不影响后续章节。
第五回合:环境变量和 PATH——系统的查找路径
"你刚才敲了 whoami,它找到了。你敲了 ls,它也找到了。"工坊主人不知道什么时候坐到了你的旁边。"但你想过没有——电脑是怎么知道 ls 在哪的?你的终端里并没有一个叫 ls 的程序躺着。"
你愣了一下。"对哦……我从来没考虑过这个问题。"
"你身上有一张地图。"工坊主人指了指你的胸口。"你走到哪都带着它,但你自己看不见。"
你身上可能背着一些"隐形的东西"——环境变量。
环境变量是 Shell 维护的一组键值对,告诉程序一些关键信息:你的用户目录在哪、用哪个编辑器、去哪里找可执行文件。
# 查看所有环境变量
env | head -10
# 查看单个变量
echo $HOME
echo $USER
echo $SHELL
echo $PATH语言:Shell (Bash/Zsh) 如何运行:直接输入 预期输出:你的家目录路径、用户名、Shell 路径、命令搜索路径 你试试:echo $LANG 看系统语言,echo $PWD 看当前目录
注意 $ 符号——在 Shell 里,$变量名 表示引用这个变量。之前提示符上写的是 ~ 而不是 $HOME,它们大部分时候是等价的。
重点来了——PATH。
# 看看 PATH 里有些什么
echo $PATH
# 预期输出示例:
# /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/home/steven/.local/bin语言:Shell (Bash/Zsh) 如何运行:直接输入 预期输出:一串用冒号隔开的目录路径 你试试:which ls 查看 ls 命令具体在哪个目录,which python3 看 Python 在哪
PATH 是 Shell 用来寻找可执行程序的"地图"。当你输入 ls,Shell 按顺序遍历 PATH 里的每个目录:
- 先查
/usr/local/bin里有没有ls - 没有?查
/usr/bin→ 找到了!执行它
如果在两个目录里找到同名文件,只执行第一个。 这就是为什么你安装新程序后,有时需要把它的目录加到 PATH 前面,才能覆盖系统默认版本。
设置自己的环境变量:
# 设置一个变量(只在当前终端会话有效)
export MY_VAR="Hello from my shell"
echo $MY_VAR
# Hello from my shell要让它永久生效,需要写入 Shell 的配置文件——Bash 是 ~/.bashrc,Zsh 是 ~/.zshrc:
# 用 echo 添加一行到配置文件
echo 'export MY_VAR="Hello from my shell"' >> ~/.bashrc
# 重新加载配置文件
source ~/.bashrc语言:Shell (Bash/Zsh) 如何运行:先手动执行 export 测试,再把那行写入配置文件 你试试:把 ~/my-bin 加入 PATH:export PATH="$HOME/my-bin:$PATH"(先测试,再写入配置文件)
进阶继续: 通配符让你批量操作文件,很实用但不是第一步就得学会的内容。
第六回合:通配符——批量施法
你建好了目录,放好了文件。然后你发现一个问题:你有二十个日志文件需要改后缀名,或者三十个测试文件要移动到一个子目录里。
你一个一个敲文件名——敲到第五个的时候,手酸了。
"你听说过工坊里的那种施法道具吗?"工坊主人从墙上取下一根铁棒。"它能同时影响一排武器,而不是一次只碰一把。"
你有一堆文件要处理,一个一个来太慢了。Shell 的通配符就是为此而生。
# 先创建一些测试文件
touch report-2024-01.txt report-2024-02.txt report-2024-03.txt
touch photo-vacation.jpg photo-party.jpg note.txt
# 查看所有 report 文件
ls report-*
# 预期输出:
# report-2024-01.txt report-2024-02.txt report-2024-03.txt
# 查看所有 .txt 文件
ls *.txt
# 查看所有以 photo- 开头的图片
ls photo-*.jpg
# 查看名字里带数字的
ls *[0-9]*语言:Shell (Bash/Zsh) 如何运行:先创建文件,然后尝试各种通配符 预期输出:根据通配符匹配的文件列表 你试试:ls report-2024-0[13].txt 只匹配 1 和 3,跳过 2
通配符匹配的规则:
| 模式 | 匹配规则 | 例子 | 匹配 |
|---|---|---|---|
* | 任意字符(0个或多个) | *.txt | 所有 txt 文件 |
? | 单个任意字符 | report-?.txt | report-1.txt, report-a.txt |
[abc] | 集合中的一个 | report-[13].txt | report-1.txt, report-3.txt |
[a-z] | 范围 | [a-z]*.txt | 以小写字母开头的 txt |
# 批量操作示例:把所有 .png 文件后缀改为 .jpg
# 先模拟一下——单纯重命名
for file in *.png; do
mv "$file" "${file%.png}.jpg"
done
# 安全做法:先打印,不改动
for file in *.png; do
echo "mv -- '$file' '${file%.png}.jpg'"
done
# 确认无误后去掉 echo语言:Shell (Bash/Zsh) 如何运行:在包含 png 文件的目录中执行 注意:先用 echo 测试,确认无误后再去掉 echo 执行真的移动
进阶继续: 管道是 Unix 哲学的精华,但可以先只读概念部分,具体命令组合遇到时再查。
第七回合:管道——把命令串起来
"你有了目录、文件、通配符——但有一件事你还没试过:把两个命令的能量连在一起。"
工坊主人拿起两根铁管,一头接在一起。"如果锻造炉的火能直接通到淬火池——你就不用端着烧红的铁块跑过去了。"
他拍了拍接好的管道。"命令也是。一个命令的输出,可以是下一个命令的输入。中间不用停下来看。"
这是你学到的最强大的工具之一。
管道符号 | 做一件事:把左边命令的输出,当成右边命令的输入。
想象一条传送带——水从上游流下来,经过不同的过滤器,最后变成纯净水。
# 找当前目录所有文件,只数个数
ls | wc -l
# 找一个进程
ps aux | grep chrome
# 看系统日志最后 5 行里包含 error 的
tail -100 /var/log/syslog | grep -i error语言:Shell (Bash/Zsh) 如何运行:逐条输入 预期输出:第一条输出文件数量,第二条列出包含 chrome 的进程 你试试:ls -la | sort -k5 -n | tail -3 找出当前目录下最大的三个文件
管道可以把无穷多的命令组合起来,每个命令只做一件事、做好它——这是 Unix 哲学的核心。
举个例子——你想知道:"当前目录里最大的 .log 文件是哪个?"
# 一步一步拆解:
# 1. 所有 .log 文件
ls *.log
# 2. 带大小显示
ls -lh *.log
# 3. 按大小排序(人类可读排序要加 -h)
ls -lh *.log | sort -k5 -h
# 4. 只看最大的那个
ls -lh *.log | sort -k5 -h | tail -1
# 5. 只保留文件名(不要大小那些信息)
ls -lh *.log | sort -k5 -h | tail -1 | awk '{print $NF}'语言:Shell (Bash/Zsh) 如何运行:在含有 .log 文件的目录中执行 你试试:先用 ls -lh *.log,一步一步加管道,理解每一步发生了什么
你并不需要记住所有的命令和选项——awk、sort、grep 你以后会逐渐熟悉。关键是理解管道的思维模式:把问题拆成小步骤,每一步输出结果,交给下一步处理。
常见陷阱
陷阱一:rm -rf / —— 不可逆的删除操作
你肯定听说过这个梗。rm -rf / 会递归删除根目录下的所有东西,意味着整个系统被清空。
但现代系统有保护——你跑不了的:
# 安全!不会执行的
rm -rf /
# rm: it is dangerous to operate recursively on '/'即使你要强行跑,也需要 --no-preserve-root。但即便如此,因为很多目录被系统锁定,你最多只能破坏自己的用户数据。
真正危险的是你自己的误操作:rm -rf ./build,但你在根目录 ~ 而不是项目目录里。养成先 pwd 和 ls 再 rm 的习惯。
陷阱二:文件名有空格
# 问题:这个文件有名有姓
touch My Important File.txt
# 试图删除
rm My Important File.txt
# rm: cannot remove 'My': No such file or directory
# rm: cannot remove 'Important': No such file or directory
# rm: cannot remove 'File.txt': No such file or directoryShell 把空格当作参数分隔符。上面的命令试图删除三个文件:My、Important、File.txt。
解决方案:永远对文件名加引号。
rm "My Important File.txt"
# 或用反斜杠转义空格
rm My\ Important\ File.txt陷阱三:~ 不是当前目录
在终端里,~ 永远是你的家目录,不是当前目录。如果你 cd /tmp,然后 rm -rf ~/build,你删掉的不是 /tmp/build,而是 /home/steven/build。
如果需要在当前目录操作,用 . 明确表示:
rm -rf ./build # 当前目录下的 build
rm -rf build # 也是当前目录下的 build(没有 /)
rm -rf ~/build # 家目录下的 build——完全不是一个地方!陷阱四:管道和重定向混合时的困惑
# 这会把 error 输出也重定向到文件
command > output.txt 2>&1
# 这表示只重定向标准输出
command > output.txt2>&1 的意思是:文件描述符 2(stderr,标准错误输出)重定向到文件描述符 1(stdout,标准输出)的当前位置。
这不是你现在就需要记住的。但当你看到 > 和 | 搭配使用时,知道有"输出流"这个概念就好。
通关挑战
- 🗡 热身(5 分钟,必做)
- 打开终端,用
pwd确认当前位置,用ls看看里面有什么。 - 在家目录下创建一个叫
practice的目录,进去,在里面创建一个叫hello.txt的文件,写入一行文字,查看它。 - 运行
echo $PATH,用:隔开的路径里,找出至少一个你知道的目录(比如/bin或/usr/bin)。 - 用
ls / | sort查看根目录有什么文件,用| head -10只看前 10 个。
- 挑战(30 分钟,选做)
管道链练习:找到
/var/log目录下最新的 3 个日志文件:bashls -lt /var/log | head -4 | tail -3理解每一步发生了什么。
批量重命名:在
practice目录下创建 10 个测试文件test-{1..10}.txt,然后用通配符把.txt改名为.bak。先用echo测试命令,确认无误再执行。bash# 创建文件 touch test-{1..10}.txt # 测试重命名 for f in test-*.txt; do echo mv "$f" "${f%.txt}.bak"; done # 真的执行 for f in test-*.txt; do mv "$f" "${f%.txt}.bak"; done探索 PATH:用
which找到python3、grep、ls分别在哪个目录。用file命令查看它们的文件类型。
- 排障:下面每个命令都有问题——你能看出问题在哪吗?
# 问题 1:
rm -rf ~/build
# (你本意想删当前目录下的 build)
# 问题 2:
cd ~
rm -r practice
# (一切都消失了——等等,你在哪?)
# 问题 3:
ls -l | grep .txt
# (咦,所有文件都出来了,不只是 .txt)
# 提示:grep 的参数是正则表达式,`.` 表示任何字符
# 正确做法:
ls -l * .txt # 错,空格问题
ls -l *.txt # 对,通配符验收标准
- 你能用
cd、ls、pwd自如地在文件系统中导航。 - 你能用
touch、mkdir、cp、mv、rm创建和管理文件目录。 - 你理解 PATH 是什么,知道
$引用变量的含义。 - 你会用
*、?通配符做批量操作。 - 你会用
|把命令串联起来。 - 你知道文件名中的空格需要用引号包裹。
- 你知道
rm -rf的危险性,养成了先ls后删的习惯。 - 你知道
>和>>的区别(覆盖 vs 追加)。
常见卡点
"我按上下箭头没反应?" 上下箭头可以调出历史命令。试几次。如果不行,确认你用的确实是 Bash 或 Zsh。
"我输入 cd .. 没反应?" cd .. 不会显示任何输出——它只是改变了当前目录。用 pwd 确认你到哪了。
"我怎么知道一个命令在哪里?" 用 which <命令名> 查看。如果返回 /usr/bin/ls,说明它就在那里。如果返回 "not found",你的 PATH 里没有它,或者你没装。
"配置文件我改乱了怎么办?" 大多数系统有备份。比如 ~/.bashrc 如果弄坏了,可以复制 /etc/skel/.bashrc 回来,或者新建一个终端会话看看有没有原始配置。如果完全坏了,创建一个干净的:
echo "# Default bashrc" > ~/.bashrc
source ~/.bashrc"为什么我改了 ~/.bashrc 没效果?" 修改后需要重新加载。运行 source ~/.bashrc 或在当前终端里新开一个子 shell。也可以直接关闭并重新打开终端。
现在不需要理解
- Shell 脚本编程(for 循环、if 条件、函数)——这章只用了最简单的 for 循环做批量操作示例
awk、sed、xargs等复杂文本处理命令——以后自然会知道/dev/null到底是什么——现在知道它是"黑洞"就行- 进程管理
kill信号类型(SIGTERMvsSIGKILL)——第 1 章提过了,但你不需要记住所有信号 - 正则表达式——
grep支持正则,但这章只用到了普通文本搜索 - 如何自定义 PS1 提示符——酷但还不需要
旅人笔记
终端是你的对话窗口。Shell 解析你的命令,
$在等你下一条。cd让你在地图上移动,ls环顾四周,pwd确认位置。
文件操作铭刻于心:touch 创建,mkdir 建营,rm 销毁但要慎之又慎。
PATH 是 Shell 找程序的目录列表,$取出变量的值。
通配符是批量施法,管道是传送带。
空格是参数分隔符,用引号包住文件名。
忘不掉的是:先ls,确认路径,再执行破坏性操作。
→ 下一站预告
你已经能熟练地在终端里穿梭了——创建、删除、移动、查找,不在话下。
但很快你就会遇到一个让所有开发者都头疼的问题:改了代码,改错了,想回到一小时前——可是已经保存了。
下一章,你会拿到时光机:Git。你可以任意回溯到任何一个时刻,像从未犯错一样重新出发。还记得工坊主人说的那句话吗——"改坏了别怕,我们有存档。"