使用grep计算出现的总数


215

grep -c这对于查找字符串在文件中出现的次数很有用,但是每行仅对一次出现进行计数。如何计算每行多次出现?

我正在寻找比以下更优雅的东西:

perl -e '$_ = <>; print scalar ( () = m/needle/g ), "\n"'

4
我知道grep已经指定了,但是对于任何使用的人ack,答案都是ack -ch <pattern>
凯尔·斯特兰德

Answers:


302

grep -o只会输出匹配项,而忽略行;wc可以算一下:

grep -o 'needle' file | wc -l

这也将匹配“针”或“多针”。
只有一个字:

grep -o '\bneedle\B' file | wc -l
# or:
grep -o '\<needle\>' file | wc -l

6
请注意,这需要GNU grep(Linux,Cygwin,FreeBSD,OSX)。
吉尔(Gilles)

@wag什么魔法也\b\B这里做?
极客

6
@Geek \ b匹配单词边界,\ B不匹配单词边界。如果在两端都使用\ b,则上面的答案会更正确。
Liam

1
要计算每行的出现次数,请与grep -n选项和uniq -c ... grep -no'\ <needle \>'文件结合使用。uniq -c
jameswarren

@jameswarren uniq仅删除相邻的相同行,如果您不确定重复项将始终紧邻sortuniq则需要在输入之前删除。
三人房

16

如果您有GNU grep(始终在Linux和Cygwin上,有时在其他地方),则可以grep -o计算输出行grep -o needle | wc -l

使用Perl,以下是我发现比您更优雅的几种方法(即使在修复后)。

perl -lne 'END {print $c} map ++$c, /needle/g'
perl -lne 'END {print $c} $c += s/needle//g'
perl -lne 'END {print $c} ++$c while /needle/g'

仅使用POSIX工具,如果可能的话,一种方法是在将输入传递给grep之前,通过一次匹配将输入分成几行。例如,如果您要查找整个单词,则首先将每个非单词字符都换行。

# equivalent to grep -ow 'needle' | wc -l
tr -c '[:alnum:]' '[\n*]' | grep -c '^needle$'

否则,没有标准命令来执行此特定的文本处理,因此您需要转向sed(如果是受虐狂)或awk。

awk '{while (match($0, /set/)) {++c; $0=substr($0, RSTART+RLENGTH)}}
     END {print c}'
sed -n -e 's/set/\n&\n/g' -e 's/^/\n/' -e 's/$/\n/' \
       -e 's/\n[^\n]*\n/\n/g' -e 's/^\n//' -e 's/\n$//' \
       -e '/./p' | wc -l

下面是使用一个简单的解决方案sedgrep,这适用于字符串或者甚至这书正则表达式,但在少数极端案例与锚定模式失败(如发现两次出现^needle\bneedleneedleneedle)。

sed 's/needle/\n&\n/g' | grep -cx 'needle'

请注意,在上面的sed替换中,我曾经\n表示换行符。这是模式部分的标准功能,但是在替换文本中,为了便于移植,请使用反斜杠换行符\n


4

如果像我一样,您实际上想要“两个;每个正好一次”(实际上是“两个;每个都两次”),那么这很简单:

grep -E "thing1|thing2" -c

并检查输出2

这种方法的好处(如果只需要一次那就是它很容易扩展)。


我不确定您是否真的要检查它只出现一次?您正在寻找的只是这些单词中的任何一个至少存在一次。
史蒂夫·戈尔

3

使用awk和needle作为字段分隔符的另一种解决方案:

awk -F'^needle | needle | needle$' '{c+=NF-1}END{print c}'

如果要匹配needle后跟标点符号,请相应地更改字段分隔符,即

awk -F'^needle[ ,.?]|[ ,.?]needle[ ,.?]|[ ,.?]needle$' '{c+=NF-1}END{print c}'

或使用class:[^[:alnum:]]包含所有非字母字符。


请注意,这需要一个支持regexp字段分隔符的awk(例如GNU awk)。
吉尔(Gilles)

1

您的示例仅打印出每行出现的次数,而不显示文件中的总数。如果这就是您想要的,则可能会执行以下操作:

perl -nle '$c+=scalar(()=m/needle/g);END{print $c}' 

您是对的-我的示例仅计算第一行中的出现次数。

1

这是我的纯bash解决方案

#!/bin/bash

B=$(for i in $(cat /tmp/a | sort -u); do
echo "$(grep $i /tmp/a | wc -l) $i"
done)

echo "$B" | sort --reverse
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.