“这行命令跑了一晚,日志全丢了?”—— 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 是"分身有术"(屏幕和文件我都要)。

相关推荐
gis分享者11 小时前
请解释 Shell 脚本中的位置参数(positional parameter)及其使用方法(中等)
shell·参数·使用·parameter·位置·positional
卡尔特斯1 天前
Zsh、GitBash 终端增强指南(Windows、Mac/Liunx)
shell
stella·2 天前
服务器割接,我所学习到的内容。
linux·运维·服务器·学习·shell·割接
柏木乃一3 天前
进程(7)命令行参数与环境变量
linux·服务器·shell·环境变量·鸣潮
Neolnfra3 天前
Shell攻防完全手册:从反弹到后渗透
渗透·shell·反弹shell
gis分享者5 天前
Bash 中如何使用正则表达式进行文本处理?(中等)
正则表达式·bash·shell·文本·处理
gis分享者6 天前
请编写一个 Bash 脚本检查系统中的所有服务状态(中等)
bash·shell·服务·状态·检查·所有
_OP_CHEN6 天前
【Linux系统编程】(十七)揭秘 Linux 进程创建与终止:从 fork 到 exit 的底层逻辑全解析
linux·运维·服务器·操作系统·shell·进程·进程创建与终止
pr_note7 天前
via ladder
shell·tcl