“这行命令跑了一晚,日志全丢了?”—— 5分钟彻底搞懂 Linux I/O 重定向与 tee 大法
1. 引言:午夜心碎时刻
你有没有经历过这样的场景:
临下班前,你写好了一个数据迁移脚本,在终端敲下命令,看着进度条开始跑,心满意足地回家了。 第二天一早,你满怀期待地打开电脑,发现终端窗口因为网络波动断开了(Broken Pipe),或者被系统的自动更新重启了。
结果: 屏幕上的日志没了,文件里也没存。脚本到底跑完了吗?报错了吗?在哪断的? 答案: 上帝才知道。
这就是**“数据流失”**的惨痛教训。
今天要讲的 >、>>、& 和 tee,就是 Linux 世界里的管道工工具包。学会它们,你就能随心所欲地控制程序输出的去向——既能在屏幕上看“现场直播”,又能把日志存进文件“录像回放”。
2. 概念拆解:Linux 的水管系统
在 Linux 的哲学里,“一切皆文件”。程序的输入输出,本质上就是三根水管。
核心三要素
我们把一个运行的程序想象成一台净水器:
-
标准输入 (stdin, 0):进水管。通常是键盘输入,或者别的文件传进来的数据。
-
标准输出 (stdout, 1):出水管(净水)。程序正常运行产生的打印信息,默认流向你的显示器。
-
标准错误 (stderr, 2):排污管(废水)。程序报错时的警告或错误信息,默认也流向你的显示器。
符号图解
-
>(覆盖重定向): 相当于把出水管从显示器上拔下来,插到一个桶(文件)里。 注意:每次都会先把桶倒空,再接水。 -
>>(追加重定向): 也把出水管插到桶里。 区别:如果桶里原本有水,它不倒掉,直接接着往里灌。 -
|(管道): 把前一台净水器的出水管,直接接到下一台净水器的进水管。 -
tee(T型三通管): 这就是神奇的神器。它是一个T型接头。水流过来,一份流向屏幕(让你看),一份分流进文件(存盘)。
3. 动手实战:从青铜到王者
我们先创建一个极简的脚本 demo.sh 来模拟真实场景。这个脚本会同时产生“正常日志”和“错误报错”。
# demo.sh
# 模拟正常输出 (stdout)
echo "✅ [INFO] 处理数据块 1..."
echo "✅ [INFO] 处理数据块 2..."
# 模拟报错 (stderr) - 注意这里我们强制输出到 stderr
echo "❌ [ERROR] 连接数据库失败!" >&2
echo "✅ [INFO] 任务结束。"
场景一:初级——直接存文件 (>)
你只想把结果存起来,不关心屏幕看没看见。
bash demo.sh > result.log
-
结果:
-
result.log里只有✅ [INFO]...。 -
等等! 屏幕上居然打印了
❌ [ERROR]...?
-
-
为什么?
-
因为
>默认只接管了 1号水管 (stdout)。 -
2号水管 (stderr) 依然指向屏幕,所以报错漏出来了,没存进文件里!
-
场景二:进阶——全量保存 (2>&1)
这是新手最头疼的符号。我们想把“报错”和“正常日志”都存进同一个文件。
bash demo.sh > result.log 2>&1
-
代码解析:
-
> result.log:先把 1号管 接到文件。 -
2>&1:这是一个“并管”操作。意思是:把 2号管 (stderr) 的出口,汇入到 1号管 (stdout) 当前的流向中。 -
结果:正常日志和报错都乖乖进了
result.log。
-
场景三:王者——我全都要 (tee)
这就是解决引言中痛点的终极方案。既要在屏幕上监控进度,又要存文件备份。
我们需要一个 T型三通(Tee)。
bash demo.sh | tee result.log
-
发生了什么?
-
|:管道符,把demo.sh的输出传给tee命令。 -
tee:接收到数据后,左手画圆(打印到屏幕),右手画方(写入 result.log)。
-
-
注意:
tee默认只接收 stdout。如果也要保存报错,需要先合并流:
# 完美方案:标准输出+错误,既看屏幕,又存文件
bash demo.sh 2>&1 | tee result.log
场景四:不讲武德——追加模式 (tee -a & >>)
如果你不想覆盖昨天的日志,而是想接着写:
-
普通重定向:用
>>代替>。 -
Tee 模式:用
-a(append) 参数。
bash demo.sh 2>&1 | tee -a result.log
4. 进阶深潜:那些你不知道的坑
坑 1:sudo 也不好使?
你可能遇到过这种情况:想把一段文本写入一个只有 root 权限才能写的文件(比如 /etc/profile)。
# ❌ 错误示范
sudo echo "export JAVA_HOME=..." >> /etc/profile
# 报错: Permission denied
为什么? 因为 sudo 只对 echo 命令生效。后面的 >> 是由你当前的 Shell 执行的,而你的 Shell 没有权限写 /etc/profile。
✅ 最佳实践 (使用 tee):
echo "export JAVA_HOME=..." | sudo tee -a /etc/profile
这里 sudo 提升了 tee 的权限,tee 负责写文件,完美解决。为了不让屏幕多打印一遍,可以扔进黑洞:
echo "data" | sudo tee file.txt > /dev/null
坑 2:2>&1 > file vs > file 2>&1
这两个命令看起来很像,但效果完全不同!顺序非常重要!
-
正确:
> file 2>&1-
解释:先把 1 指向文件,然后把 2 指向 1(也就是文件)。
-
结果:大家都在文件里。
-
-
错误:
2>&1 > file-
解释:先把 2 指向 1(此时 1 还在屏幕),所以 2 去了屏幕。然后把 1 指向文件。
-
结果:报错打在了屏幕上,只有正常日志进了文件。
-
5. 总结与延伸
一句话总结
-
>是“洗心革面”(覆盖文件)。 -
>>是“再续前缘”(追加文件)。 -
2>&1是“兄弟齐心”(错误流汇入标准流)。 -
tee是“分身有术”(屏幕和文件我都要)。
浙公网安备 33010602011771号