“这行命令跑了一晚,日志全丢了?”—— 5分钟彻底搞懂 Linux I/O 重定向与 tee 大法

1. 引言:午夜心碎时刻

你有没有经历过这样的场景:

临下班前,你写好了一个数据迁移脚本,在终端敲下命令,看着进度条开始跑,心满意足地回家了。 第二天一早,你满怀期待地打开电脑,发现终端窗口因为网络波动断开了(Broken Pipe),或者被系统的自动更新重启了。

结果: 屏幕上的日志没了,文件里也没存。脚本到底跑完了吗?报错了吗?在哪断的? 答案: 上帝才知道。

这就是**“数据流失”**的惨痛教训。

今天要讲的 >>>&tee,就是 Linux 世界里的管道工工具包。学会它们,你就能随心所欲地控制程序输出的去向——既能在屏幕上看“现场直播”,又能把日志存进文件“录像回放”。


2. 概念拆解:Linux 的水管系统

在 Linux 的哲学里,“一切皆文件”。程序的输入输出,本质上就是三根水管。

核心三要素

我们把一个运行的程序想象成一台净水器

  1. 标准输入 (stdin, 0):进水管。通常是键盘输入,或者别的文件传进来的数据。

  2. 标准输出 (stdout, 1):出水管(净水)。程序正常运行产生的打印信息,默认流向你的显示器

  3. 标准错误 (stderr, 2):排污管(废水)。程序报错时的警告或错误信息,默认也流向你的显示器

符号图解

  • > (覆盖重定向): 相当于把出水管从显示器上拔下来,插到一个桶(文件)里。 注意:每次都会先把桶倒空,再接水。

  • >> (追加重定向): 也把出水管插到桶里。 区别:如果桶里原本有水,它不倒掉,直接接着往里灌。

  • | (管道): 把前一台净水器的出水管,直接接到下一台净水器的进水管。

  • tee (T型三通管): 这就是神奇的神器。它是一个T型接头。水流过来,一份流向屏幕(让你看),一份分流进文件(存盘)。


3. 动手实战:从青铜到王者

我们先创建一个极简的脚本 demo.sh 来模拟真实场景。这个脚本会同时产生“正常日志”和“错误报错”。

Bash
 
# demo.sh
# 模拟正常输出 (stdout)
echo "✅ [INFO] 处理数据块 1..."
echo "✅ [INFO] 处理数据块 2..."

# 模拟报错 (stderr) - 注意这里我们强制输出到 stderr
echo "❌ [ERROR] 连接数据库失败!" >&2

echo "✅ [INFO] 任务结束。"

场景一:初级——直接存文件 (>)

你只想把结果存起来,不关心屏幕看没看见。

Bash
 
bash demo.sh > result.log
  • 结果

    • result.log 里只有 ✅ [INFO]...

    • 等等! 屏幕上居然打印了 ❌ [ERROR]...

  • 为什么?

    • 因为 > 默认只接管了 1号水管 (stdout)

    • 2号水管 (stderr) 依然指向屏幕,所以报错漏出来了,没存进文件里!

场景二:进阶——全量保存 (2>&1)

这是新手最头疼的符号。我们想把“报错”和“正常日志”都存进同一个文件。

Bash
 
bash demo.sh > result.log 2>&1
  • 代码解析

    • > result.log:先把 1号管 接到文件。

    • 2>&1:这是一个“并管”操作。意思是:把 2号管 (stderr) 的出口,汇入到 1号管 (stdout) 当前的流向中。

    • 结果:正常日志和报错都乖乖进了 result.log

场景三:王者——我全都要 (tee)

这就是解决引言中痛点的终极方案。既要在屏幕上监控进度,又要存文件备份。

Getty Images

我们需要一个 T型三通(Tee)。

Bash
 
bash demo.sh | tee result.log
  • 发生了什么?

    • |:管道符,把 demo.sh 的输出传给 tee 命令。

    • tee:接收到数据后,左手画圆(打印到屏幕),右手画方(写入 result.log)

  • 注意tee 默认只接收 stdout。如果也要保存报错,需要先合并流:

Bash
 
# 完美方案:标准输出+错误,既看屏幕,又存文件
bash demo.sh 2>&1 | tee result.log

场景四:不讲武德——追加模式 (tee -a & >>)

如果你不想覆盖昨天的日志,而是想接着写:

  • 普通重定向:用 >> 代替 >

  • Tee 模式:用 -a (append) 参数。

Bash
 
bash demo.sh 2>&1 | tee -a result.log

4. 进阶深潜:那些你不知道的坑

坑 1:sudo 也不好使?

你可能遇到过这种情况:想把一段文本写入一个只有 root 权限才能写的文件(比如 /etc/profile)。

Bash
 
# ❌ 错误示范
sudo echo "export JAVA_HOME=..." >> /etc/profile
# 报错: Permission denied

为什么? 因为 sudo 只对 echo 命令生效。后面的 >> 是由你当前的 Shell 执行的,而你的 Shell 没有权限写 /etc/profile

✅ 最佳实践 (使用 tee)

Bash
 
echo "export JAVA_HOME=..." | sudo tee -a /etc/profile

这里 sudo 提升了 tee 的权限,tee 负责写文件,完美解决。为了不让屏幕多打印一遍,可以扔进黑洞:

Bash
 
echo "data" | sudo tee file.txt > /dev/null

坑 2:2>&1 > file vs > file 2>&1

这两个命令看起来很像,但效果完全不同!顺序非常重要!

  1. 正确: > file 2>&1

    • 解释:先把 1 指向文件,然后把 2 指向 1(也就是文件)。

    • 结果:大家都在文件里。

  2. 错误: 2>&1 > file

    • 解释:先把 2 指向 1(此时 1 还在屏幕),所以 2 去了屏幕。然后把 1 指向文件。

    • 结果:报错打在了屏幕上,只有正常日志进了文件。


5. 总结与延伸

一句话总结

  • > 是“洗心革面”(覆盖文件)。

  • >> 是“再续前缘”(追加文件)。

  • 2>&1 是“兄弟齐心”(错误流汇入标准流)。

  • tee 是“分身有术”(屏幕和文件我都要)。

posted @ 2025-12-30 16:43  Swizard  阅读(125)  评论(1)    收藏  举报