Bash 单行读取 stdin 并打印最后 10 个匹配项,每个匹配项对应 2 个单独的模式,无需使用 tmp 文件 /awk/perl/等

Bash 单行读取 stdin 并打印最后 10 个匹配项,每个匹配项对应 2 个单独的模式,无需使用 tmp 文件 /awk/perl/等

编辑:一般来说,我要求一种优雅的方式来复制输入流,用 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()输出并在//中解析它:STDERRSTDOUTtee

让我们开始吧,可能是一个一句话另外,为了便于阅读,使用多行:

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

有关的:http://mywiki.wooledge.org/ProcessSubstitution

答案2

使用和好的开关:

$ 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

相关内容