Loading...
墨滴

尤慕

2021/05/15  阅读:46  主题:雁栖湖

有趣的 tee

Part1有趣的 tee:Linux 、Go 以及 Java

文章发于背井公众号:有趣的 tee:Linux 、Go 以及 Java

Linux

在运行 Linux 命令时,有时我们想在看到运行结果的同时,又能把结果保存到文件中。

  1. 小白用户可能会这样操作:
# 1. 运行命令
echo hello world
# 2. 将结果复制,然后打开 vim ,将刚才的内容粘贴进去保存。
  1. 稍稍有经验的人可能会这样做:
# 1. 运行命令并将结果重定向到文件
echo hello world > my.file
# 2. 查看运行结果
cat my.file

而本篇讲述的 tee,它是一个 Linux 命令,它从标准输入读取数据并回显到标准输出,同时将其写入到一个或更多的文件中。所以,

  1. 一个有经验的开发会这样解决题述要求:
# 简单到只要这样
echo hello world | tee my.file

如果只是写到这里,未免不值用一篇文章来介绍。
实际上,这里要介绍的是一种思想(或实践?):编程过程中偶尔会遇到对一个输入做多种处理的情况,上面的 tee 命令即是对这种场景的封装运用。

其它语言也有类似的例子。

Go

如果你了解 Go 语言,很可能已经知道它的标准 i/o 库中有个 TeeReader,它的签名如下:

func TeeReader(r Reader, w Writer) Reader

等你知道它的用途,一定会觉得它和 Linux 下的 tee 命令很像:它返回一个新的 Reader,在读取 r 的同时,会将读取结果写入到 w

你可能会好奇它的实际用途是什么。举个例子,有些流只能读取一次,再读时会报错。HTTP request body 也是这种流的一种。

试想,如果我们想在服务端将 request body 打印到日志中,由于它只能被读一次,日志服务读取并打印完它后,后续的业务代码要再次读取并解析 request body 就会报错。一个解决方式即是用 TeeReader

而在 Java 中,如果要完成同样的打印 request body 的操作且不影响后续的业务,可能要自己写实现,或者利用第三方库了。

不过,Java 中还有其它有关 tee 的这种场景(一个输入多种操作)。

Java

之前的一篇文章有介绍怎么从一个 Stream 中取出多值:自定义 Stream Collector 的一个现实例子:从 stream 中获取多个值

针对这种需求,Java 在其版本 12 中也提供了一个 Collectors.teeing() 方法,它的签名是:

public static <T,R1,R2,R> Collector<T,?,R> teeing(Collector<? super T,?,R1> downstream1, Collector<? super T,?,R2> downstream2, BiFunction<? super R1,? super R2,R> merger)

(啊,Java确实繁琐!)

如果你熟悉 Collector,可以很容易猜到,这个方法最终是把两个 collector 的结果值,借助 BiFunction 进行合并返回。这样做的好处是,它既避免了自己去写自定义的 Collector 实现,又能复用 jdk 自身提供的 Collectors 工具方法。

总结

对于 一个输入&多种处理需求 这样的场景,文章提供了3个语言环境下的相应实践,供大家思考借鉴:自己碰到类似的需求时,如何封装一个简单易用的 API?

尤慕

2021/05/15  阅读:46  主题:雁栖湖

作者介绍

尤慕