简单的解决方案

简单的解决方案

我有一个看起来像这样的文件

header start
stuff
header end
pos LV file LVG size
1   a1 AAA  BBB 100
2   b1 AAC  BBB 1000
3   a3 AAB  BBB 47
4   b6 AAC  BBB 1000

a我需要计算以第二列开头的行。谷歌搜索我找到了这个

awk '/LVG/{p=$0} {a[p]++} END{for(i in a) print i"\n"a[i]-1}' file

但我需要过滤内容,因此输出只是行数。我添加了以下内容

awk '/LVG/ || $2 ~ "^a"' file | awk '/LVG/{p=$0} {a[p]++} END{for(i in a) print i"\n"a[i]-1}' | tail -1

这满足了我的需要,但我想知道这一切是否可以用一个awk命令来完成。

答案1

最基本的要求是通过满足

awk '$2~/^a/{c++} END{print c+0}'

这将检查第二列;通过正则表达式比较检查它是否以开头a,并增加一个计数器c。在文件末尾它将打印计数器。为了确保即使c从未实际增加的数字也会被打印,如果它已经非零,我们打印c+0它不会改变,但在仍未初始化时强制解释为数字。cc

现在,为了确保“标题”部分没有“杂散”标记干扰,第一个检查是确保第一列是整数:

awk '$1+0==$1 && $2~/^a/{c++} END{print c+0}'

这里的想法是,如果是一个数字,则$1+0在算术上将被解释为“不添加任何内容” ,但会指示$1awk追加字符0如果它是“文本”,则仅当它是数字$1+0时才会改变。$1

对于更复杂的检查,我们可以禁止考虑行,直到遇到“header end”行,此外还可以立即跳过该行:

awk 'f==2&&$2~/^a/{c++} f==1&&NF{f++} $0=="header end"{f=1} END{print c+0}'

当遇到该行时,这会将标志设置f为 1 ,将其设置为标题后的第一个非空行,并且仅在检查第二列的行中。header end2f2

答案2

我提出的不是一个awk解决方案,而是grep一个依赖于记录结构的解决方案:

$ grep "^[0-9]\+[[:blank:]]\+a[0-9]\+[[:blank:]]\+[A-Z]\+[[:blank:]]\+[A-Z]\+[[:blank:]]\+[0-9]\+$" file | wc -l
2

答案3

awk 'NR==1 ,/^header end$/ { next };
    !skip_hdr              { skip_hdr=1; next }
    ($2 ~ /^a/)            { count++ }
END{ print count+0 }' infile
  • NR==1 ,/^header end$/ { next }
    从文件开头跳到第一行header end;这会跳过以下几行:

    标头开始
    东西
    头尾
    

  • !skip_hdr { skip_hdr=1; next }:
    跳过标题行pos LV file LVG size

  • ($2 ~ /^a/) { count++ }
    计算第二列以a字符开头的行数(不区分大小写)。

  • END{ print count+0 }
    打印最终计数值。

答案4

简单的解决方案

符合您的描述:计算以第二列开头的行这就够了:

awk '$2 ~ /^a/ { count++ } END {print count}' file 

甚至可以使用 grep (应该更快)命令:

grep -c '^[0-9][0-9]* *a' test.txt

这也恰好更具体,因为它要求第一个字段是数字(假设这是您从发布的示例中需要的)并且字段分隔符是空格。

看得更深入

但您发布的第一个解决方案:

awk '/LVG/{p=$0} {a[p]++} END{for(i in a) print i"\n"a[i]-1}' file

做了一些完全不同的事情。

在此文件上输入:

> cat file
header start
stuff
header end
pos1 LV file LVG size
1   a1 AAA  BBB 100
2   b1 AAC  BBB 1000
3   a3 AAB  BBB 47
4   b6 AAC  BBB 1000
5   c9 BBA  CBA 20
pos2 LV file LVG size
1   a1 AAA  BBB 100
2   b1 AAC  BBB 1000
pos3 LV file LVG size
1   a1 AAA  BBB 100
2   b1 AAC  BBB 1000
3   a3 AAB  BBB 47

您发布的代码打印每个(不同)标头的结果:

> awk '/LVG/{p=$0} {a[p]++} END{for(i in a) print i"=="a[i]-1}' test.txt 
==2
pos1 LV file LVG size==5
pos3 LV file LVG size==3
pos2 LV file LVG size==2

即:2 行用于空标题,5 行位于标题“pos1”之后,等等......

我不知道你是否需要这种计数。我也不清楚为什么要从计数中减去 1。

唯一的附加要求是仅计算第二个字段以a

$ awk '/LVG/{p=$0} $2 ~ /^a/ {a[p]++} END{for(i in a) print i"=="a[i]}' test.txt
pos1 LV file LVG size==2
pos3 LV file LVG size==2
pos2 LV file LVG size==1

如果您还需要第一个字段为数字(类似于上面的 grep 命令):

$ awk '/LVG/{p=$0} ($1+0!=0)&&($2~/^a/) {a[p]++} END{for(i in a) print i"=="a[i]}' test.txt
pos1 LV file LVG size==2
pos3 LV file LVG size==2
pos2 LV file LVG size==1

相关内容