shell 什么时候替你做了什么
一句话总结
考试中命令表现与预期不同,大多数时候问题不在命令,而在于Shell 在执行命令之前做了什么。展开时机、文件描述符、find 的表达式语法——这三项构成基础命令领域的骨架。
为什么需要了解这一点
执行 rm -f /var/log/*.gz 后,文件却没有删除。人们会怀疑 rm,但罪魁祸首并不是 rm。把通配符转换成实际文件列表的是 Shell,而且 Shell 在调用命令之前就完成了这一步。若没有匹配文件,Shell 会把 *.gz 原样作为参数传递,rm 只能回答不存在这个名称的文件。
引号的含义也在这里产生差异。双引号保留变量展开,只阻止分词和文件名展开;单引号则阻止全部展开。处理包含空格的路径时,遗漏双引号会让一个参数被拆成两个。考试题目故意在路径中放空格,原因就在这里。
工作原理
重定向就是操作文件描述符。 所有进程启动时都有 0(标准输入)、1(标准输出)、2(标准错误)。> out.txt 表示“把 1 号描述符指向的位置改为这个文件”;2>&1 表示“把 2 号复制为 1 号当前所指的位置”。
顺序之所以重要,关键就在“当前”二字。
| 写法 | 结果 |
|---|---|
cmd > f 2>&1 |
先把 1 改到 f,再把 2 复制到 f → 两者都进入文件 |
cmd 2>&1 > f |
先把 2 复制到终端,再只把 1 改到 f → 错误仍显示在屏幕 |
find 不是一条普通命令,而是一种表达式语言。 -name、-size、-mtime、-perm 不是选项,而是返回真假的谓词;依次排列时,会以隐式 AND 连接。-print 与 -exec 也是谓词,只不过是带副作用的谓词。因此用 -o 表示 OR 时,如果省略括号,就会因优先级错误而匹配到意外文件。
数字参数还有正负号规则。-mtime 30 表示“正好 30 天前的时间段”,-mtime +30 表示“超过 30 天”,-mtime -30 表示“30 天以内”。-perm 644 要求完全匹配,-perm -644 要求包含全部这些位,-perm /644 则只要包含其中任意一位。这是考试中最容易失分的地方。
实际工作中的表现
作者关于文件描述符的文章举过这样的案例:日志文件被误删,但打开它的进程仍然运行。目录项虽然已经消失,链接数也变成 0,只要打开的描述符还在,内核就不会回收数据块。因此,只需复制 /proc/<PID>/fd/<번호> 就能恢复全部内容。反过来,也可以利用同一性质,通过 truncate -s 0 /proc/<PID>/fd/<번호> 在不停止进程的情况下释放空间;但若认错描述符编号,就会把正常文件清空。
在管道中,yes | head -1 是经典示例。head 先结束后,yes 会向已经没有读取端的管道写入,最终因 SIGPIPE 终止。这是正常行为。作者的 Shell 文章还反复强调一条原则:不要解析 ls 输出,应使用 glob 遍历。 文件名一旦包含空格或换行,解析 ls 就会悄悄出错。
避免在考场上手忙脚乱
LFCS 是在两小时内手工完成任务的考试,因此知道答案与能在限时内敲出来是两回事。把少数动作练成习惯,就能大幅节省时间。
提前确定从哪里查文档。 虽然不能访问互联网,但仍有 man 和 --help。只要会在 man 中用 / 搜索、用 n 跳到下一个结果,寻找一个选项只需几秒。若不知道可通过 man -k 从关键词反查命令,仅仅想不起命令名称就会浪费数分钟。
先确认不可逆操作。 rm、mkfs、dd、> 都不会询问确认。尤其重定向会在执行命令的瞬间先清空目标文件,因此读取原文件并写回同一文件的形式总会破坏内容。应经过临时文件,或使用原地编辑选项。
修改前先制作副本。 编辑配置文件时,附上日期复制一份只需两秒;没有副本时,回滚可能需要数分钟。考试和实际工作都一样。
完成验证才算完成任务。 “启用服务”通常同时要求它现在运行,以及重启后仍能启动,这是两个不同设置。创建用户的任务通常还包含 Shell、主目录和组。在自认为已经答完的位置多花 30 秒确认,比全部做完后再找错误便宜得多。
最后,卡住就先跳过。 考试按部分得分;纠结一道题时,可能错过两道本来会做的题。跳过时记下留下了什么,若还有时间再回来。
下个练习将做什么
在 /root/lfcs-ess/ 下建立目录树,亲自确认硬链接与符号链接的 inode 差异。组合 find 的大小、时间和权限条件生成列表;分别用 gzip 与 xz 压缩同一目录并比较大小。最后通过 diff -u 制作补丁,再应用到副本。后续练习还会用 grep、sed、awk、jq、yq 处理亲自生成的日志。