数据流重定向

LinuxBeginner
立即练习

简介

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.txtabsent.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

此时文件应包含 gammabetaalphadelta。当你需要实时查看终端反馈,同时保存日志时,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 脚本。