tee 命令详解与参数实测(Ubuntu 22.04)
复制标准输入的 tee:默认覆盖与 -a 追加、多文件分流、管道中间落盘、-p 管道错误不中断的耗时对照实测、/dev/null 用法与 echo 配 sudo tee 写 root 文件的原理
tee 命令详解与参数实测(Ubuntu 22.04)
目标:全面掌握 GNU tee 的「复制标准输入」能力——默认写入文件同时透传标准输出的覆盖行为、
-a追加、一次写多个文件、管道中间落盘(命令 | tee 日志 | 下游)、-p管道错误不中断的真实语义(含下游提前退出的耗时对照)、/dev/null与向配置文件追加的场景用法,以及echo xx | sudo tee file写 root 文件的原理说明,全部命令与回显已在 Ubuntu 22.04(GNU coreutils 8.32)实测通过。
一、环境信息
本节列出本文所有实测所依赖的环境与测试数据,便于读者按同规格复现。
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
- tee 版本:GNU coreutils 8.32(
tee --version实测) - tee 属于 coreutils 基础工具集,Ubuntu 22.04 默认预装,无需额外安装
- 演示目录:
/root/demo/tee/ - 测试数据:
base.txt(seq 生成的 1 到 5 共 5 行)作为基准文件;实测过程陆续生成out.txt、f1.txt、f2.txt、seq.log、nums.log、root-test.conf等产物文件
二、命令概览
2.1 语法与测试数据准备
tee 的基本语法是 tee [OPTION]... [FILE]...:把标准输入复制到每个 FILE,同时继续写到标准输出。它的名字来自管道工程的三通接头(T 形三通)——一路进、两路出。与 > 重定向的根本区别在于:> 把数据流整个改道(屏幕上不再出现),tee 则是「留一份在文件、照常往后传」,因此能插在管道中间而不打断数据流。
准备演示目录与基准文件:
rm -rf /root/demo/tee && mkdir -p /root/demo/tee
seq 1 5 > /root/demo/tee/base.txt
验证基准文件内容:
$ cat /root/demo/tee/base.txt
1
2
3
4
5
2.2 --help 全量回显
tee --help 输出全部参数清单(实测原样保留,可作为参数速查表):
Usage: tee [OPTION]... [FILE]...
Copy standard input to each FILE, and also to standard output.
-a, --append append to the given FILEs, do not overwrite
-i, --ignore-interrupts ignore interrupt signals
-p diagnose errors writing to non pipes
--output-error[=MODE] set behavior on write error. See MODE below
--help display this help and exit
--version output version information and exit
MODE determines behavior with write errors on the outputs:
'warn' diagnose errors writing to any output
'warn-nopipe' diagnose errors writing to any output not a pipe
'exit' exit on error writing to any output
'exit-nopipe' exit on error writing to any output not a pipe
The default MODE for the -p option is 'warn-nopipe'.
The default operation when --output-error is not specified, is to
exit immediately on error writing to a pipe, and diagnose errors
writing to non pipe outputs.
参数不多,核心是 -a(追加)与 -p(写错误时的行为调整)。帮助下半段详细规定了写错误的四种 MODE:默认行为是「写管道出错立即退出、写非管道(普通文件)出错仅诊断后继续」,-p 的默认 MODE 是 warn-nopipe——这正是 3.5 节实测的理论依据。
2.3 版本确认
确认实测环境的 tee 版本:
$ tee --version | head -1
tee (GNU coreutils) 8.32
三、参数逐项实测
3.1 默认行为:写文件同时透传,且是覆盖写
echo 'first run' | tee out.txt 的输出同时出现在屏幕与文件里——透传是 tee 的本职:
$ echo 'first run' | tee /root/demo/tee/out.txt
first run
$ cat /root/demo/tee/out.txt
first run
默认按 > 的语义覆盖已有内容:再次写入同样的目标文件,旧内容消失:
$ echo 'second run' | tee /root/demo/tee/out.txt
second run
$ cat /root/demo/tee/out.txt
second run
💡 说明:tee 不带参数时只透传到标准输出;FILE 缺省时它是「纯透传」,带文件时是「复制」。第一次使用最容易踩的坑是把 tee 当追加用——默认是覆盖,追加必须显式
-a。
3.2 -a:追加写入
-a(--append)把覆盖语义改为追加。实测在 out.txt(当前内容 second run)后追加一行:
$ echo 'appended line' | tee -a /root/demo/tee/out.txt
appended line
$ cat /root/demo/tee/out.txt
second run
appended line
帮助原文写明该行为对「给定的所有 FILE」生效:append to the given FILEs, do not overwrite——同时写多个文件并追加时(见 3.3),每个目标文件都是追加。
3.3 一次写多个文件
FILE 参数可以给多个,同一份输入会复制到每一个目标文件。实测同时写 f1.txt 与 f2.txt:
$ echo 'multi target' | tee /root/demo/tee/f1.txt /root/demo/tee/f2.txt
multi target
$ cat /root/demo/tee/f1.txt /root/demo/tee/f2.txt
multi target
multi target
两个文件各收到一份完整内容。典型用途:一次生成两份相同的快照(如同时落盘到数据目录与备份目录)。
3.4 管道中间落盘
tee 最经典的形态是插在管道中间:上游数据先存一份日志,再原样交给下游统计。实测统计 seq 输出行数的同时把数据留在 seq.log:
$ seq 1 10 | tee /root/demo/tee/seq.log | wc -l
10
$ head -3 /root/demo/tee/seq.log
1
2
3
wc -l 得到 10 行,seq.log 里也完整保存了 1 到 10。若把 tee 换成 > seq.log,下游 wc 将拿不到任何数据——这就是 tee 存在的意义:分流而不截流。
💡 技术拓展:「终端可见 + 文件留存」的输出分流写法对比——
方式 写法 数据流走向 tee 命令 | tee 日志 | 下游分流不截流:终端、文件、下游三路都有(本文主角) 重定向 >命令 > 日志整体改道:终端不可见、下游无数据 追加重定向 命令 >> 日志同 >追加写,管道中间同样不可用需要插在管道中间落盘时 tee 是标准解;「只要文件不要回显」用
>即可(见五、Q2)。
3.5 -p:管道错误不中断
-p 调整「某个输出写失败」时的行为。先实测目标文件不可写的场景:不带 -p 与带 -p,tee 都会诊断错误并继续把数据透传到标准输出,退出码都是 1:
$ echo 'without -p' | tee /no/such/dir/x.txt; echo 退出码=$?
tee: /no/such/dir/x.txt: No such file or directory
without -p
退出码=1
$ echo 'with -p' | tee -p /no/such/dir/x.txt; echo 退出码=$?
tee: /no/such/dir/x.txt: No such file or directory
with -p
退出码=1
下游也照常收到数据(两种情况 wc -c 都收到完整的 10 字节):
$ echo 'pipe test' | tee /no/such/dir/x.txt | wc -c
tee: /no/such/dir/x.txt: No such file or directory
10
$ echo 'pipe test' | tee -p /no/such/dir/x.txt | wc -c
tee: /no/such/dir/x.txt: No such file or directory
10
-p 真正的用武之地是管道输出失败——典型是下游提前退出(如 head -1 拿到第一行就关管道)。帮助原文写明:默认模式「写管道出错立即退出」,-p 的默认 MODE 是 warn-nopipe(写非管道出错仅诊断、写管道出错不退出)。用 1000 万行数据实测下游提前退出时的耗时差异,不带 -p 时 tee 被立即终止,0.005 秒收场:
$ time seq 1 10000000 | tee /dev/null | head -1
1
real 0m0.005s
user 0m0.003s
sys 0m0.005s
带 -p 时 tee 无视断掉的管道继续读完上游全部输入,耗时 0.213 秒:
$ time seq 1 10000000 | tee -p /dev/null | head -1
1
real 0m0.213s
user 0m0.120s
sys 0m0.083s
💡 说明:这个对照给出了
-p的准确定义——它不是让 tee 忽略错误,而是让 tee 在管道下游断开时不退出、继续处理完上游数据。日志管道里希望「下游慢或断开也不丢日志」时加-p;反之默认行为让整条管道尽早止损,通常更符合直觉。
3.6 /dev/null:只透传不留档
FILE 可以是设备文件。目标是 /dev/null 时,复制走的那一份被系统丢弃,tee 退化为纯透传(常用于临时测试或占位):
$ echo 'discard copy' | tee /dev/null
discard copy
四、实战示例
4.1 中间落盘 + 下游过滤的组合
把 3.4 的形态用于实际统计:20 行数字先存档到 nums.log,再交给下游过滤统计以 1 开头的行数:
$ seq 1 20 | tee /root/demo/tee/nums.log | grep -c '^1'
11
下游计数 11(1、10 到 19),nums.log 完整保留 1 到 20 供复查。这类「落一份、算一份」的流水线广泛用于构建日志、巡检脚本与数据抽查。
4.2 向配置文件写入与追加
tee 复制流的语义天然适合「既看一眼写入了什么、又落盘确认」。实测先生成一份配置文件:
$ echo 'conf value' | tee /root/demo/tee/root-test.conf
conf value
$ cat /root/demo/tee/root-test.conf
conf value
需要追加配置行时加 -a,屏幕回显与文件内容始终一致:
$ echo 'conf line 2' | tee -a /root/demo/tee/root-test.conf
conf line 2
$ cat /root/demo/tee/root-test.conf
conf value
conf line 2
⚠️ 注意:把同样的写入目标换成
/etc/hosts、/etc/sysctl.conf这类 root 权限文件时,脚本里的标准姿势是echo xx | sudo tee -a /etc/xx而不是sudo echo xx > /etc/xx——因为>重定向由当前普通用户的 shell 完成,sudo 只提权了 echo、提权不到重定向;而 tee 本身被 sudo 以 root 身份执行,真正打开文件写入的是 root 进程。本文演示目录在 root 家目录下无需 sudo,故直接实测了 tee 本身。
五、注意事项与常见问题
Q1:echo xx | sudo tee file 为什么能写入 root 文件而 sudo echo xx > file 不行?
> 是 shell 的语法,重定向在 sudo 启动 echo 之前就由当前普通用户 shell 执行了,权限不足直接失败;管道右侧的 tee 被 sudo 以 root 启动,打开并写入文件的是这个 root 进程。一句话记住:sudo 提权的是 tee 进程,不是重定向。
Q2:tee 和 > 的本质区别是什么?
> 是改道:数据不再流向原本的标准输出;tee 是复制:原标准输出照常、另抄一份给文件。由此推论——> 不能出现在管道中间(会终结数据流),tee 可以;需要「边看边存」用 tee,需要「只要文件」用 >。
Q3:tee 默认覆盖还是追加?
默认覆盖(3.1 节实测第二次写入后旧内容消失),与 > 行为对齐;追加必须显式 -a(3.2 节实测)。反复执行的同一条命令带不带 -a,结果分别是「最后一行」与「全部历史行」,写巡检脚本时务必区分。
Q4:-p 实测下来到底改变了什么?
对普通文件写失败,默认与 -p 都是「诊断后继续、退出码 1」(3.5 节第一组实测);差别只在管道输出:下游提前退出时,默认的 tee 立即退出止损(1000 万行 0.005 秒),-p 的 tee 继续处理完上游(0.213 秒)。是否需要 -p 取决于「下游断开时,上游数据是丢是保」这个业务决定。
Q5:同时写多个文件时,某个目标失败会怎样?
帮助原文的 MODE 机制在起作用:默认对普通文件类错误「诊断后继续」,其余目标文件与标准输出不受影响;把 MODE 调成 --output-error=exit 则任一输出失败立即退出。本文实测覆盖默认行为(3.5 节),--output-error 的四种 MODE 取值见 2.2 节帮助原文。
六、参考链接
- GNU coreutils 官方手册(tee 章节):https://www.gnu.org/software/coreutils/manual/html_node/tee-invocation.html
- GNU coreutils 首页:https://www.gnu.org/software/coreutils/tee
- 本机帮助:
tee --help、man tee(Ubuntu 22.04 内置)