用 tee 把命令输出同时写进文件:从分流到追加

举报
yd_28502004 发表于 2026/11/09 07:48:58 2026/11/09
【摘要】 在终端里工作时,我们常常遇到一个两难:命令的输出既想实时看在屏幕上,又想留一份到文件里备查。最朴素的做法是先运行、再重定向,可那样就看不到实时输出;或者把输出重定向到文件、事后再 cat,可那样又失去了实时感。tee 就是为这个场景而生的小工具:它像水管上的三通接头,让数据流一边继续往前走,一边分出一路写进文件。 一、tee 的基本用法:分流而不截断tee 的名字就来自管道配件 T 形三通(...

在终端里工作时,我们常常遇到一个两难:命令的输出既想实时看在屏幕上,又想留一份到文件里备查。最朴素的做法是先运行、再重定向,可那样就看不到实时输出;或者把输出重定向到文件、事后再 cat,可那样又失去了实时感。tee 就是为这个场景而生的小工具:它像水管上的三通接头,让数据流一边继续往前走,一边分出一路写进文件。

一、tee 的基本用法:分流而不截断

tee 的名字就来自管道配件 T 形三通(Tee)。它从标准输入读取数据,把同一份数据同时写到标准输出和指定的文件里。最常见的写法是把它接在管道中间:

ls -lh /var/log | tee file-list.txt

这条命令执行后,ls 的结果会照常显示在终端,同时完整地写进 file-list.txt。再看一个更贴近日常的例子——编译或跑测试时既要看日志、又要存档:

make 2>&1 | tee build.log

注意这里的 2>&1:它先把标准错误合并到标准输出,tee 才能把报错信息也一并收进日志。只写 make | tee build.log 时,报错仍会直接打印到终端,却不会进文件,这是一个常见的坑。

默认情况下,tee 会清空目标文件再写入——也就是覆盖模式。如果你只是想先看一眼输出、顺手存一份,这个行为没问题;但如果文件里已经有之前的内容,覆盖就会把旧记录丢掉。

二、-a 追加:别让昨天的日志被今天覆盖

tee 的 -a(append)选项可以把写入方式改成追加,在文件末尾续写,而不是清空重写:

date | tee -a record.log
echo "开始备份" | tee -a record.log

两条命令执行完,record.log 里会保留两行记录。写轮询脚本、定时任务日志时,-a 几乎是必选项:

./backup.sh 2>&1 | tee -a /var/log/backup.log

每天跑一次,日志就按时间顺序自然累积,排查问题时按时间翻查即可。需要提醒的是,追加并不会自动帮你做日志轮转,文件会一直长大;长期运行的任务要配合 logrotate 之类工具定期切分,否则迟早会把磁盘写满。

三、与管道组合:一次输出,多处留存

tee 的真正威力在管道组合里体现出来。它可以把一份输出同时发给多个文件,也可以把输出分发给后续命令做不同的处理。

一次写多个文件,只需要在 tee 后面跟多个文件名:

df -h | tee disk-today.txt disk-archive.txt

更进一步,配合进程替换,还能把同一份数据同时交给多个命令处理。例如既要统计行数、又要全文存档:

cat access.log | tee >(wc -l > line-count.txt) > full-copy.txt

这里 >(...) 是 Bash 的进程替换,它把括号里的命令伪装成一个文件,tee 把数据写进去的同时,也把原样数据通过标准输出写进了 full-copy.txt。同理,实时过滤与存档也可以并行:

tail -f app.log | tee >(grep ERROR > errors-only.log)

屏幕上照常滚动完整日志,而所有 ERROR 行会被悄悄挑出来,另存一份,方便事后只看错误。

四、sudo tee:写那些“权限不够”的文件

这是 tee 在日常运维里最实用的一个技巧。往 /etc/hosts、/etc/sysctl.conf 这类系统文件里追加内容时,直接写会遇到权限问题:

echo "127.0.0.1 dev.local" >> /etc/hosts  # 权限不足,会失败

失败的原因在于:重定向 >> 是由当前 shell(普通用户权限)打开文件的,sudo 只提升了 echo 的权限,却管不到 shell 的重定向。换成 sudo tee -a 就能绕过去:

echo "127.0.0.1 dev.local" | sudo tee -a /etc/hosts

这时以 root 权限运行的是 tee,由它来打开并写入文件,自然就写进去了。屏幕上还会回显刚写入的那一行,相当于顺手确认了一遍。

不过要提醒一句:sudo tee 不是万能钥匙。写系统文件之前,先确认内容无误,改错关键配置可能导致服务异常;能用系统自带的配置工具时,优先用配置工具。

五、tee 与进程替换:把输出同时喂给多个命令

进程替换值得单独再展开一点,因为它让 tee 从“分流到文件”升级为“分流到命令”。语法上,>(...) 产生一个可写的文件描述符,<(...) 产生一个可读的文件描述符:

# 一份输出同时排序存档和去重计数
sort data.txt | tee >(uniq -c > counts.txt) > sorted.txt

# 对比两份命令输出,不用先落盘
 diff <(ls dir-a) <(ls dir-b)

第一个例子里,排序后的数据同时去了两个地方:一路进 uniq -c 统计重复次数,另一路原样存进 sorted.txt。第二个例子虽然没有直接用到 tee,但用的是同一种“把命令输出当文件”的思路,两者常常配合出现。

需要留意的是,进程替换是 Bash、Zsh 等高级 shell 的特性,sh(dash)并不支持;写可移植脚本时要先确认目标环境的 shell。

六、使用时的几个小提醒

  • 先确认再覆盖:不带 -a 的 tee 会清空文件,目标文件是重要数据时,先用 cp 备份或直接用 -a。
  • 标准错误要先合并:想把报错也写进文件,记得在管道前加 2>&1,否则错误信息会漏网。
  • 缓冲问题:管道里的输出可能被缓冲,tail -f | tee 实时性不够时,可以给上游命令加 --line-buffered(如 grep --line-buffered)或用 stdbuf -oL。
  • 退出状态:管道的退出状态默认是最后一个命令的;关心上游命令是否成功时,用 set -o pipefail,并通过 ${PIPESTATUS[0]} 取上游退出码。
  • 权限与归属:用 sudo tee 创建的文件属于 root,后续普通用户再想追加,需要继续用 sudo 或改一下文件归属。

结语

tee 只有十几 KB,却把 Unix“小工具组合”的哲学体现得很彻底:它不生产数据,只负责分流。记住三个要点——默认覆盖、加 -a 追加、配合 2>&1 与进程替换,就能把“边看边存”“一份输出多处用”这类日常需求处理得干净利落。下次再遇到“既想看输出、又想留文件”的场景,不妨先想想 tee。

本文为原创技术短文,内容由作者根据日常 Linux 使用经验整理,示例均在常见 Linux 发行版的 Bash 环境中验证思路,转载请注明出处。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。