【问题标题】:Truncate middle of piped text and replace with ellipsis in one command在一个命令中截断管道文本的中间并用省略号替换
【发布时间】:2025-12-21 08:05:17
【问题描述】:

我有一个很长的文本流,开始发送到stdout。在 bash 中,我可以通过运行来显示前几行:

$ seq 1 5010 | head 5
1
2
3
4
5

还是结束

$ seq 1 5010 | tail 5
5006
5007
5008
5009
5010

但是我需要显示前 5 个和后 5 个以及中间的省略号...所以输出可能看起来像

1
2
3
4
5
...
5006
5007
5008
5009
5010

我需要在一个单行的股票 bash 中执行此操作。那可能吗?怎么样?

【问题讨论】:

  • head -n 5 file; echo ...; tail -n 5 file 但是,更严重的是,使用 awk,打印前五行,保留五行的滑动窗口并在最后打印出来。
  • file.txt 只有 10 行或更少行时,你想要什么输出?
  • 该文件将始终超过 10 行。我通过标准输出获取输入,无法直接访问文件。
  • @Schneems:请将该信息编辑到您的问题中。这是一个重要的说明,因此不应在评论中。
  • @EtanReisner:顺便说一下,head 不能保证只读取请求的行数,这意味着head -n5; 可能会吸收超过五行的内容。试试seq 1862

标签: bash shell


【解决方案1】:

这是一个 bash (v4) 解决方案:

seq 1 5010 | { mapfile -tn 5 a; printf "%s\n" "${a[@]}" ...; tail -n 5; }

在 v3 上,以下内容应该可以工作(虽然它的行要长得多):

seq 1 5010 | {
  for x in 1 2 3 4 5; do IFS= read -r; echo "$REPLY"; done;
  echo ...;
  tail -n 5;
}

【讨论】:

  • 酷,应该指定。我在本地使用3.2.53,在生产中使用4.1.5(1)-release。刚刚在生产中验证,效果很好!
  • @Schneems:v3 解决方案即将推出
【解决方案2】:

Perl:

seq 5010 | perl -0nE '@lines = split /\n/; say join "\n", @lines[0..4], "...", @lines[-5..-1]'
1
2
3
4
5
...
5006
5007
5008
5009
5010

等效的 awk:

awk -v RS="" '{
    for (i=1; i<=5; i++) print $i
    print "..."
    for (i=NF-4; i<=NF; i++) print i
}'

这是 Etan 的建议。比我的解决方案更节省内存:

seq 5010 | awk '
    NR <= 5 
    {l5=l4; l4=l3; l3=l2; l2=l1; l1=$0} 
    END {print "..."; print l5; print l4; print l3; print l2; print l1}
'

【讨论】:

  • 然而,您对我建议的解决方案的实施比我的可能要优雅得多。 =)