很多时候,在使用命令行时,我发现自己在某些输入流指定的一堆不同实例上指定相同的操作,然后想要以某种特定的方式重新组合它们的输出。
昨天的两个用例:
我想查看捆绑在 PEM 文件中的一堆 SSL 证书的主题。
cat mypemfile.crt | (openssl x509 -noout -text | fgrep Subject:)
只显示第一个。我需要拆分证书并对每个证书运行相同的命令,然后连接结果。
csplit
可以拆分它们,但只能拆分为文件。这就麻烦了。为什么我不能直接说
cat mypemfile.crt | csplit-tee '/BEGIN/ .. /END/' | on-each ( openssl x509 -noout -text | fgrep Subject: ) | merge --cat
?
我们运行一个 JupyterHub 实例,将笔记本服务器拆分为 Docker 容器。我想查看他们带时间戳的日志。对于一个容器来说这很容易:
sudo docker logs -t -f $container_id
(
-t
添加时间戳,并保持-f
管道打开,就像tail -f
。)列出所有容器的日志很容易,按时间戳排序:
sudo docker ps | awk '{print $1}' | while read container_id do sudo docker logs -t $container_id done | sort
或者
sudo docker ps | awk '{print $1}' | xargs -n1 sudo docker logs -t | sort
或者
sudo docker ps | awk '{print $1}' | parallel sudo docker logs -t {} | sort
但这些都不会让我使用该
-f
选项来观看日志。为什么我不能直接使用
sudo docker ps | awk '{print $1}' | csplit-tee /./ | on-each (xargs echo | sudo docker logs -t -f) | merge --line-by-line --lexicographically
或者
sudo docker ps | awk '{print $1}' | parallel --multipipe sudo docker logs -t -f {} | merge --line-by-line --lexicographically
?
显然,这需要
- 特定的外壳支持。也许我们需要一个独特的“多管道”符号。
- 用于拆分和合并管道的新工具(
csplit-tee
、on-each
和)。merge
- 关于如何在工具内指定任意多个输入和输出文件描述符的固定约定,以便该 shell 将它们视为并行管道。
这已经做到了吗?或者我可以应用于我的用例的等效内容?
没有特定内核支持是否可行?我知道内核通常有一个固定的最大数量的打开文件描述符,但是一个实现可以通过不盲目地尝试一次打开它们来解决这个问题。
知道的话可行吗GNU并行可行吗?
答案1
使用 GNU 并行:
cat mypemfile.crt |
parallel --pipe -N1 --recstart '-----BEGIN' 'openssl x509 -noout -text | fgrep Subject:'
未经测试:
sudo docker ps | awk '{print $1}' |
sudo parallel -j0 --lb docker logs -t -f {}
sudo docker ps | awk '{print $1}' |
sudo parallel -j0 --tag --lb docker logs -t -f {}
答案2
不,外壳不能做到这一点。管道只是从源到目的地的流,通常在不同的进程中。
让我们看看你的第一个例子:
cat mypemfile.crt |
(openssl x509 -noout -text | fgrep Subject:)
您希望输入文件沿着包含 BEGIN 和 END 的行进行分割,但cat
不关心内容,并且管道无论如何都没有记录分隔符的越界指示。
您可以通过管道的源和目标中的特定约定来实现此目的,但这会消除管道的主要优点,即能够将程序组合为构建块,以完成其作者未预期的任务。
openssl
在此特定示例中,修改以处理一个流中的多个证书比修改openssl
以处理多个流并修改cat
以提供这些多个流要容易得多。