编辑:一般来说,我要求一种优雅的方式来复制输入流,用 2 组或更多组命令处理它,然后合并这些命令的输出。强调优雅。
实际用例:在 CI 管道中,bash
脚本正在执行git fetch --tags
输出一长串标签的操作。所有标签都看起来像desktop-...
或mobile-...
。我们实际上不需要查看几个月或几年前的标签,因此我想避免打印整个列表,而是只打印最后 10 个(每个)桌面和移动标签。我可以想象一个有超过 2 个平台的场景,因此在一般情况下,我们希望能够过滤任意数量的 10 个组。
规则:
- 必须整洁优雅狂欢单行代码,而不是脚本(使用一些高级 bash 功能或(或多或少常见的)辅助实用程序来减少代码量)
- 不应使用临时文件或其他需要清理的内容
- 没有管道到 awk、perl 或类似程序
- 没有复杂的 Bash 逻辑(例如计数器变量和
while read
循环) - 必须在终端中和脱离 tty 时都能工作(比如在 CI 环境中)
- 必须匹配至少 2 个模式,并打印每个模式的最后 10 个结果
- 每个模式的结果不应该混合在一起,但是假设这个要求很宽松,只要解决方案允许将
| sort -V
完整输出作为最后一步 - 如果解决方案可以扩展到 3 种以上模式,则可获得加分
为了简化测试,我删除了所有本地标签,将输出转储git fetch --tags
到名为 tags 的文件中,并cat tags
在尝试期间使用。为了让您更轻松地测试可能的解决方案,这里有一个简单的脚本,可以生成一个看起来合理的模拟输入:
for ((i=0; i<100; ++i)); do echo "$((1+RANDOM/5000)).0.$((RANDOM/5000))"; done | sort -V | while read v; do [[ $((RANDOM%2)) == 1 ]] && echo -n "mobile-" || echo -n "desktop-"; echo $v; done >tags
注意:解决方案不应直接引用文件。如果可以的话,您可以只grep
运行两次或更多次以获得每个模式的 10 个匹配项。但在我描述的用例中,该命令git fetch --tags
仅运行一次(即使运行多次,第二次运行时也不会输出任何内容,因为第一次运行时已经获取了标签)。因此,只允许像这样使用此输入文件一次:cat tags
。这旨在模拟上面描述的真实用例。
我迄今为止最好的尝试:
cat tags | tee >(grep '\<desktop-' | tail -n 10) | grep '\<mobile-' | tail -n 10
“桌面”进程替换的问题是:其 stdout 为空,因此输出丢失。我见过的所有进程替换示例都将输出(例如命令)重定向到文件。我觉得一定有一些不错的方法可以将此类输出“合并”回单个流,但到目前为止我找不到这种方法。
产生所需输出但不符合我的简洁优雅规则的解决方案(它们都有点长:
- 一些典型的命令式编程,如果您使用“真正的”编程语言,您将编写此类代码。它不符合 UNIX shell 脚本的精神,后者使用管道和标准命令,每个命令都执行一项工作(并做得很好)以实现所需的最终结果。
d=0; m=0; tac tags | while read l; do [[ $d -lt 10 && $l =~ ^desktop- ]] && { echo $l; let ++d; }; [[ $m -lt 10 && $l =~ ^mobile- ]] && { echo $l; let ++m; } done | sort -V
- 使用管道和 bash 的进程替换,但使用临时文件。临时文件的“正确”使用通常涉及
mktemp
、事后清理、处理异常脚本终止(用于trap
对 SIGHUP/SIGINT 进行清理)。
cat tags | tee >(grep '\<mobile-' | tail -n 10 >mobile-tags) | grep '\<desktop-' | tail -n 10 && cat mobile-tags && rm -f mobile-tags
答案1
好的,今天早上收到了,plumbing
:
这个想法是复制和的dup()
输出并在//中解析它:STDERR
STDOUT
tee
让我们开始吧,可能是一个一句话另外,为了便于阅读,使用多行:
git fetch --tags | tee /dev/stderr 2> >(
grep -w mobile | tail -n10) 1> >(
grep -w desktop | tail -n10)
或者借助你的精彩评论(有点集思广益):
git fetch --tags | tee /dev/fd/{10,11,12} 10> >(
grep -w desktop | tail -n 3) 11> >(
grep -w mobile | tail -n 3) 12> >(
grep -w foobar | tail -n 3) 1>/dev/null
可以简化为:
git fetch --tags | tee >(
grep -w mobile | tail -n3) >(
grep -w desktop | tail -n3) >(
grep -w foobar | tail -n3) > /dev/null
答案2
使用grep和好的开关:
$ grep -Ewm20 'desktop|mobile' tags | sort
desktop-10.5.0
desktop-11.5.0
desktop-12.6.0
desktop-13.6.0
desktop-14.7.0
desktop-15.7.0
desktop-16.8.0
desktop-17.8.0
desktop-18.9.0
desktop-19.9.0
mobile-15.6.0
mobile-16.6.0
mobile-17.7.0
mobile-18.7.0
mobile-19.8.0
mobile-20.8.0
mobile-21.9.0
mobile-22.9.0
mobile-23.10.0
mobile-24.10.0
答案3
那这句话怎么说呢?
d=( $(grep desktop tags) ) m=( $(grep mobile tags) ); printf '%s\n' "${d[@]:0:10}" "${m[@]:0:10}" | sort -rV