Linux 标准输入输出:Bash 重定向、错误流与管道练习
Shell 命令通常有标准输入(stdin)、标准输出(stdout)和标准错误(stderr)三个通道。在 Bash 中,< 从文件提供输入,> 与 >> 把标准输出写入文件,2> 保存标准错误;管道 | 把前一命令的标准输出接到后一命令的标准输入。跟踪每个通道的去向,就能理解为什么有些结果出现在屏幕上,有些只保存在文件中。
本课说明数据流的连接方式,成功或失败时是否运行下一条命令的 &&、|| 另见Shell 条件执行流程(英语)。以下采用 Bash 语法;其他 Shell 的扩展语法可能不同,不能把所有符号都按命令名理解。
先区分三个标准流
文件描述符是进程引用已打开输入或输出通道的编号。三个标准描述符通常为 0、1、2,最初常连接终端,但也可以连接文件、管道等对象:
| 编号 | 通道 | 主要用途 |
|---|---|---|
| 0 | 标准输入 stdin | 命令读取的数据 |
| 1 | 标准输出 stdout | 常规结果 |
| 2 | 标准错误 stderr | 诊断与错误消息 |
两个输出通道在终端可能显示在一起,仍然是独立通道。> 默认只改变描述符 1,因此正常结果写进文件后,错误消息仍可能留在屏幕上。stderr 也不只用于致命错误,有些程序会把警告或进度信息写到这里,不能仅凭通道名称判断退出状态。
准备新目录并比较覆盖与追加
以普通用户在 Bash 中新建 linux-course-14-practice。同名目录若已存在,应换名并调整路径。逐条执行下面命令,mkdir 或 cd 失败就停止;pwd 应确认当前位置是新建的空目录。不要在已有资料的目录中直接继续,因为 > 通常会先清空已有目标再写入。
mkdir ~/linux-course-14-practice
cd ~/linux-course-14-practice
pwd
核对位置后,先写入两行,再用 >> 追加第三行。printf 中的 \n 表示换行,最后用 cat 检查:
printf 'alpha\nbeta\n' > source.txt
printf 'gamma\n' >> source.txt
cat source.txt
应依次看到 alpha、beta、gamma。第一个 > 不是先在屏幕显示再复制一份,而是把标准输出直接送到 source.txt;>> 在文件末尾添加,不清空前面的两行。目标不存在时,两者通常都会创建文件,前提是能够打开目标路径。
如果 Bash 启用了 noclobber,> 覆盖已有普通文件可能被拒绝。遇到保护或权限错误,应先核对目标,不要为了继续练习而改用强制覆盖;追加也会修改文件,不能当成完全没有风险的操作。
把文件接到输入或保存正常输出
< 把文件连接到标准输入,因而下面 wc 从输入流读内容,没有把 source.txt 当成文件名参数:
wc -l < source.txt
预期只显示数字 3,可能带有对齐空白。换成 wc -l source.txt 时,通常还会显示文件名。这里每条记录都以换行结束,因此 wc -l 的换行计数等于三行。
要保存匹配结果,可把 grep 的标准输出重定向到另一个新文件,再读取它:
grep 'beta' source.txt > matches.txt
cat matches.txt
grep 的匹配行进入 matches.txt,不会在执行第一行时直接显示;接着 cat matches.txt 才应显示 beta。若目标文件没有内容,不应立刻断言重定向坏了,也可能是模式未匹配或命令读取输入失败,需要结合错误信息与退出状态判断。
Shell 在命令主体运行前处理重定向。即使命令随后失败,目标文件也可能已经创建或清空。尤其不要把读取 source.txt 的命令再重定向回同一个 source.txt;保留原文件,并给结果选择另一个明确路径。
单独保存错误并检查重定向顺序
下面以新练习目录内不存在 absent-14.txt 为前提。ls 的找不到文件消息写入 stderr,用 2> 只把这个通道保存到 errors.txt:
ls absent-14.txt 2> errors.txt
cat errors.txt
运行 ls 时屏幕可能没有消息,随后 cat errors.txt 应显示找不到文件的诊断,具体文字依工具和语言环境变化。保存了错误并没有让 ls 成功;如果要检查它的退出状态,应紧跟 ls 检查,而不是等 cat 执行后再读取 $?。
Bash 中,命令 > all.txt 2>&1 可把两个输出通道指向同一个文件。2>&1 表示让描述符 2 复制描述符 1 当时的目的地,不是让 stderr 永久追踪 stdout 后续的变化:
ls absent-14.txt > all.txt 2>&1
cat all.txt
先处理 > all.txt,让 stdout 指向文件;再处理 2>&1,让 stderr 指向同一位置。所以本例的诊断也能由 cat all.txt 读到。这里没有正常列表输出,all.txt 仍可包含错误消息。
重定向从左到右处理。若改为 2>&1 > all.txt,stderr 先复制 stdout 原来的去向,之后只有 stdout 改到文件;在直接使用终端时,错误通常仍显示在屏幕上。如果原来的 stdout 已是另一个文件或管道,错误会去那个原目的地,不能一概称为“屏幕”。
2> 中编号和运算符应连在一起;写成 2 > 时,2 可能被当成命令的普通参数。文件路径若包含空格,应正确加引号,不要把带空格路径拆成多个参数。
用管道连接命令并理解状态
管道 | 把左边命令的 stdout 连到右边命令的 stdin。下面先筛选含 beta 的行,再统计这些行的换行数:
grep 'beta' source.txt | wc -l
本课数据应输出 1。普通 | 默认不把左边的 stderr 也送过去,因此错误消息可能仍出现在终端;看到右边打印数字,并不证明左边没有错误。
Bash 默认用管道中最后一个命令的退出状态作为整体状态。比如 grep 没有匹配行,wc -l 仍可正常输出 0,整体状态也可为 0;甚至前一阶段出现读取错误,后一阶段仍可能完成对空输入的计数。排查时应拆开各阶段,分别检查错误与状态。
若需要保留前面阶段的非零状态,可在合适范围内考虑 Bash 的 pipefail。开启后,整体状态取最右侧非零状态,全部成功才为 0;但“grep 没匹配”的状态 1 也会影响结果,仍需按业务含义解释。修改 Shell 选项前先了解当前设置,不要把它当成所有管道都应无条件启用的答案。
重定向改变命令与文件等通道的连接,管道连接命令之间的数据流。结果不符合预期时,先明确是 stdout、stderr 还是退出状态,再核对目标路径和处理顺序。
官方文档
文件描述符、覆盖与追加、从左到右的重定向规则,以及管道连接和 pipefail 状态,可查阅 man7 托管的上游 Bash(1) 手册的 REDIRECTION 与 Pipelines 部分,并核对本机 Bash 版本。




