简介
Linux 命令通常从终端读取输入,并将结果写回终端。Shell 可以将这些数据流重新连接到文件或其他命令,这称为重定向。重定向是日志记录、管道、后台任务和 Shell 脚本的基础。
在本实验中,你将使用标准输入、标准输出和标准错误。你将学习 > 何时覆盖文件、>> 何时追加内容、2> 和 2>&1 如何处理错误、管道如何传递数据、tee 如何同时显示并保存相同的输出,以及 /dev/null 何时有用。
了解标准输入、标准输出和标准错误
在本步骤中,你将创建一个小型输入文件,并通过标准输入将它连接到命令。
每个命令启动时都有 3 个标准数据流:
| 描述符 | 名称 | 默认连接对象 |
|---|---|---|
0 |
标准输入,即 stdin | 键盘 |
1 |
标准输出,即 stdout | 终端 |
2 |
标准错误,即 stderr | 终端 |
创建并进入工作目录:
mkdir -p /home/labex/project/streams-lab
cd /home/labex/project/streams-lab
使用 here-document 创建一个包含 3 行内容的输入文件。Shell 会将两个 EOF 标记之间的行发送给 cat,而 > 会将 cat 的输出保存到 input.txt:
cat > input.txt <<'EOF'
alpha
beta
gamma
EOF
通常,cat input.txt 会将文件名作为参数接收。< 运算符则会将该文件连接到描述符 0,也就是标准输入:
cat < input.txt
你应该会看到这 3 行内容。对 wc -l 使用同样的输入重定向。该命令用于统计行数:
wc -l < input.txt
输出应为 3。由于文件名不是 wc 的参数,因此输出中只会显示数字。
重定向并追加标准输出
在本步骤中,你将重定向命令的普通输出,并观察覆盖和追加之间的区别。
确认当前位于数据流实验工作目录:
cd /home/labex/project/streams-lab
> 运算符会将描述符 1,也就是标准输出,连接到文件。写入前,它会创建文件,或清空已有文件内容:
echo "Stream report" > report.txt
cat report.txt
再次执行覆盖操作,观察之前的行消失:
echo "Input lines: 3" > report.txt
cat report.txt
现在使用 >>。它会以追加模式打开目标文件:
echo "Status: complete" >> report.txt
cat report.txt
此时文件应正好包含两行:第一行是 Input lines: 3,第二行是 Status: complete。重定向由 Shell 在命令启动前执行。
分离标准输出和标准错误
在本步骤中,你将运行一个同时产生普通输出和错误的命令,然后将两个数据流分别保存到不同文件。
下面的 ls 命令接收一个存在的路径和一个不存在的路径:
cd /home/labex/project/streams-lab
ls -l input.txt missing.txt
描述 input.txt 的行属于 stdout。No such file or directory 消息属于 stderr。默认情况下,这两个数据流都会显示在终端中,但它们彼此独立。
使用 > 重定向描述符 1,使用 2> 重定向描述符 2:
ls -l input.txt missing.txt > listing.log 2> errors.log
由于其中一个路径不存在,该命令仍会返回非零退出状态,但 Shell 仍会准备好接收下一条命令。检查两个目标文件:
cat listing.log
cat errors.log
listing.log 应提到 input.txt,但不包含错误信息。errors.log 应提到 missing.txt 及其错误信息,但不包含成功列出的文件信息。
合并标准输出和标准错误
在本步骤中,你将把 stdout 和 stderr 写入同一个日志文件,并了解重定向顺序为何重要。
先将 stdout 重定向到 combined.log,再将描述符 2 指向描述符 1 当前使用的目标:
cd /home/labex/project/streams-lab
ls -l input.txt missing.txt > combined.log 2>&1
表达式 2>&1 表示「将描述符 2 发送到描述符 1 当前使用的同一个目标」。由于 > combined.log 先出现,因此两个数据流都会进入该文件。
cat combined.log
该文件应同时包含 input.txt 的列表信息和 missing.txt 的错误信息。
Bash 还支持使用 &> 作为重定向两个数据流的简写:
ls -l report.txt absent.txt &> shorthand.log
cat shorthand.log
第二个文件应包含成功列出的 report.txt 和 absent.txt 的错误信息。在脚本和服务命令中,具有可移植性的 > file 2>&1 形式尤其常见。
使用 Tee 查看并保存管道输出
在本步骤中,你将使用管道和 tee,在显示输出的同时将相同的数据保存到文件。
管道写作 |,它会将左侧命令的 stdout 连接到右侧命令的 stdin。按字母逆序排列输入文件:
cd /home/labex/project/streams-lab
sort -r input.txt
使用普通输出重定向时,排序后的行会写入文件,不再显示在终端中:
sort -r input.txt > sorted.txt
cat sorted.txt
tee 命令会将 stdin 同时复制到 stdout 和指定文件。通过管道运行相同的排序命令:
sort -r input.txt | tee sorted.txt
你应该会在屏幕上看到排序后的行,同时 sorted.txt 也会收到相同的内容。默认情况下,tee 会像 > 一样覆盖目标文件。
-a 选项会让 tee 追加内容。在显示新增行的同时,将它追加到文件中:
echo "delta" | tee -a sorted.txt
cat sorted.txt
此时文件应包含 gamma、beta、alpha 和 delta。当你需要实时查看终端反馈,同时保存日志时,tee 很有用。
使用 Dev Null 丢弃不需要的数据
在本步骤中,你将使用 /dev/null 有意丢弃一个数据流,使用 tee 保留有用的输出,并清空已有日志。
/dev/null 是一种特殊设备,会接收并丢弃写入其中的所有内容。列出一个真实目录和一个不存在的目录,同时只丢弃 stderr:
cd /home/labex/project/streams-lab
ls -l . missing-directory 2> /dev/null
你应该能看到目录列表,但看不到 missing-directory 的错误信息。将此操作与 tee 结合,使可见的 stdout 同时保存到文件:
ls -l . missing-directory 2> /dev/null | tee visible-listing.log
管道只传递 stdout。在管道接收数据之前,stderr 已单独重定向到 /dev/null。
检查保存的目录列表:
cat visible-listing.log
最后,创建一个可丢弃的临时日志,然后使用 /dev/null 作为空输入源来清空其内容。另一个文件会保留步骤 4 中合并数据流的证据:
echo "outdated scratch data" > scratch.log
cat /dev/null > scratch.log
检查文件大小:
ls -l scratch.log
该文件应仍然存在,大小为 0 字节。请有意识地使用丢弃操作:当你确实需要诊断信息时,隐藏错误可能会增加故障排查难度。
总结
你练习了 3 个标准数据流及其文件描述符,使用 < 将文件连接到 stdin,并使用 > 和 >> 重定向 stdout。你使用 2> 分离 stderr,使用 2>&1 和 &> 将其与 stdout 合并,并了解了运算符顺序为何重要。
你还比较了普通文件重定向与管道和 tee 的区别,使用 tee -a 在显示输出的同时追加内容,并使用 /dev/null 丢弃不需要的数据。这些模式将帮助你在后续课程中理解日志记录、后台命令和 Shell 脚本。



