Contents

Linux中Tee命令简介

1. 概述

在Unix中,我们可以pipe多个进程,一个接一个,形成管道 ,让消息在它们之间线性传递。在本教程中,我们将了解*tee *命令并将其用作管道中的 T 型分离器

2. 作为T型分离器

2.1. 基本

顾名思义,我们可以使用tee命令创建一个带有一个入口和一个或多个出口的 T 型分离器。让我们熟悉一下tee的调用语法和策略:

tee [-ai] [file ...]

本质上,入口与标准输入流相连,而其中一个出口与标准输出流相连。此外,通过在调用期间提供文件名,我们可以根据需要拥有更多的出口:

/uploads/tee_command/1.png

我们必须注意,默认情况下,tee总是覆盖文件,除非我们使用***-a*开关在追加模式下使用它**。

2.2. 试用tee

现在我们了解了基础知识,让我们看看实际的tee

我们必须注意,**通过按Ctrl+C 键,我们可以向程序发送SIGINT 信号。*并且,默认情况下,tee通过终止其执行来处理此中断。但是,如果需要,我们可以使用-i*开关禁用它。

3. tee管道

T 型分离器的更实际用途是将其与管道中的管道结合使用。通过这样做,我们可以在第二个程序转换它之前将第一个程序的中间输出捕获到一个文件中:

/uploads/tee_command/3.png

假设我们在文件grocery-supply.txt中有一个未排序的杂货清单:

$ cat grocery-supply.txt
Milk
Eggs
Almonds
Sugar
Salt

此外,我们还有两个要求需要满足:

  1. 获取列表的排序版本
  2. 从排序列表中找出第一项

由于第二个任务的输入取决于第一个任务的输出,因此使用管道将是正确的选择。在管道的每一侧,我们可以分别使用sort 和*head *命令:

$ sort grocery-supply.txt | head -1
Almonds

好吧,我们可以看到我们的输出仅包括列表排序版本中的第一项。但是,我们丢失了列表的排序版本。 对于这种情况,使用 T 型分离器非常合适:

$ sort grocery-supply.txt | tee grocery-supply-sorted.txt | head -1
Almonds
$ cat grocery-supply-sorted.txt
Almonds
Eggs
Milk
Salt
Sugar

看来我们成功了!

4. tee进程替换

到目前为止,一切都很好。现在,我们已经准备好使用这些概念作为构建块来解决更复杂的问题,这些问题涉及使用进程替换和过滤命令 ,例如*awk sort cut *等等。

4.1. 多份报告

假设我们在grocery-shopping.txt文件中维护我们的杂货店购物清单:

$ cat grocery-shopping.txt
Day-1:Milk:4
Day-3:Butter:2
Day-5:Eggs:12
Day-8:Milk:2
Day-9:Milk:2
Day-10:Eggs:6

现在,我们需要使用购物数据生成一些报告:

  • 按时间倒序排列的购物清单
  • 汇总到目前为止购买的每件商品的总量的逐项报告
  • 购物的最后一天

随着时间的推移,我们对个别报告的要求可能会发生变化。因此,我们的策略应该包含未来添加、删除和修改单个报告的用例。

现在,由于所有报告都依赖于grocery-shopping.txt的内容,我们希望最小化各个报告之间的耦合。因此,如果我们在各自的脚本中保留报告生成的逻辑,即report-quantity.shreport-rev-chronological.shreport-last-shopping-day.sh,那将是明智的:

$ ./report-quantity.sh < grocery-shopping.txt
$ ./report-rev-chronological.sh < grocery-shopping.txt
$ ./report-last-shopping-day.sh < grocery-shopping.txt

此外,我们可以在这些单独的脚本中使用cutawktail 命令来过滤和聚合值。

4.2. 过滤器和聚合器

可以使用tail命令的*-r*开关生成我们的倒序购物清单报告:

$ cat report-rev-chronological.sh
#!/bin/sh
tail -r > rev-grocery-shopping.txt

另一方面,我们的逐项数量报告可以使用**awk 数组 来循环遍历行**记录。对于每条记录,它会根据第三个输入字段( $3 )的可用数量来增加商品的数量:

$ cat report-quantity.sh
#!/bin/sh
awk -F':' \
'
{
	items[$2]+=$3
}
END {
	for (item in items) {
		reportname="quantity-"item".txt"
		print items[item] >reportname
	}
}
'

结束块 中,每个项目的数量被写入一个名为quantity-item.txt 的文件中。

最后,我们的最后一天购物报告可以结合使用尾部剪切过滤器:

$ cat report-last-shopping-day.sh
#!/bin/sh
tail -1 | cut -f1 -d':' > last-shopping-day.txt

4.3. 过程替代

**我们可以使用tee命令和进程替换 来有效地使用公共数据源,**而不是分别调用每个报告。好吧,我们的想法是,我们可以将标准输入直接提供给进程,而不是将标准输入写入标准输出和文件:

$ tee >(prog1) >(prog2) >(prog3) ...

因此,我们现在可以使用流程替换和tee调用我们的每个报告生成脚本:

$ cat grocery-shopping.txt \
| tee \
>(./report-quantity.sh) \
>(./report-rev-chronological.sh) \
>(./report-last-shopping-day.sh) \
1 >/dev/null

由于所有报告都写入各自的报告文件,因此我们不需要在屏幕上打印任何内容。因此,最后一行丢弃了从 stdout 到/dev/null 设备的所有内容。

最后,让我们验证生成的报告:

$ grep '.*' quantity-* last-shopping-day.txt rev-grocery-shopping.txt
quantity-Butter.txt:2
quantity-Eggs.txt:18
quantity-Milk.txt:8
last-shopping-day.txt:Day-10
rev-grocery-shopping.txt:Day-10:Eggs:6
rev-grocery-shopping.txt:Day-9:Milk:2
rev-grocery-shopping.txt:Day-8:Milk:2
rev-grocery-shopping.txt:Day-5:Eggs:12
rev-grocery-shopping.txt:Day-3:Butter:2
rev-grocery-shopping.txt:Day-1:Milk:4