我有一个看起来像这样的文件
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
它不会改变,但在仍未初始化时强制解释为数字。c
c
现在,为了确保“标题”部分没有“杂散”标记干扰,第一个检查是确保第一列是整数:
awk '$1+0==$1 && $2~/^a/{c++} END{print c+0}'
这里的想法是,如果是一个数字,则$1+0
在算术上将被解释为“不添加任何内容” ,但会指示$1
awk
追加字符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 end
2
f
2
答案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