如果至少有 n 行,则仅通过管道输出

如果至少有 n 行,则仅通过管道输出

我经常想要 grep 包含一些标题行的进程的输出。但如果 grep 会删除所有实际内容行,我不想显示标题行。为此,我需要一个类似于的命令tail,但它不是只返回 n 行,而是返回全部行,但前提是至少有 n 行(标题)。

一位同事编写了一个小型 Python 工具来执行此操作,但需要在我需要执行此操作的所有计算机上安装它。

是否有一个简短的命令行可以使用标准工具(也许是 awk?)实现这种效果?

答案1

您可以将第一n行保存在 awk 数组中,并在(如果)看到 line 时将它们写出来n + 1,即当您发现至少有一个数据行时。

Awk_N='
FNR <= N { X[FNR] = $0; next; }
FNR == N + 1 {
    for (j = 1; j in X; ++j) print X[j];
    delete X;
}
{ print; }
'

awk -v N=7 "${Awk_N}"

我创建了7一个 shell 空间变量,这样就可以在不编辑 Awk 部分的情况下替换它。

Awk 部分被预先声明为单引号多行 shell 字符串变量,以整理您的管道。

awk您可能还会考虑是否可以同时将 grep 逻辑折叠到 中。

稍微简短的版本,存储所有行。

awk '{ X[NR] = $0 } END { if (NR > 3) for (j = 1; j in X; ++j) print X[j] }'

这可以最大限度地减少字符数(以牺牲可读性为代价)。

awk '{X[NR]=$0}END{if(NR>3)for(j=1;j in X;++j)print X[j]}'

答案2

这是sed一行(假设您想打印所有内容,如果至少有 4 行,即n=4):

cmd | sed -e '4,$!{H;1h;d;}' -e '4H;4x'

所以,如果你想使用变量

cmd | sed -e "${n}"',$!{H;1h;d;}' -e "${n}H;${n}x"

这样做的作用是将第一n-1行保存在H旧缓冲区中并d从模式空间中删除它们,因此不会打印任何内容...除非sed接收到至少一行 - 第一n行,并将其添加到H旧空间中,然后x更改缓冲区所以现在模式空间包含1到 的行n。剩下的就是自动打印的问题了。
在某些非 GNU 设置上,您可能需要这样编写(假设n=21这次):

cmd | sed '21,$!{
H;1h;d
}
21H;21x'

答案3

如果标头有 7 行长,如果输入长度 <= 7,我们可以将其删除。使用 Perl 或 (gnu)sed:

cmd... | perl -0pe 's/^(.*\n){,7}$//'
cmd... | sed   -zE 's/^(.*\n){,7}$//m'

(避免使用千兆字节输入执行此操作)

答案4

我设法使用teeand解决了这个问题wc

cmd | tee x | [ `wc -l <&0` -gt 3 ] && cat x && rm x

在本例中,如果超过 3 行,则输出整个输出。

NB1 当您只需要知道是否超过 3 行时,计算整个文件的行数就显得有些过分了。

NB2 进一步降低性能,这会写入一个临时文件。据我了解,使用mkfifo会使它进入内存,但命令会更长。

相关内容