web-dev-qa-db-ja.com

grepを使用して出現回数の合計を数える

grep -cは、ファイル内で文字列が出現する回数を見つけるのに役立ちますが、1行に1回しか出現しません。行ごとに複数の出現を数える方法は?

私はよりエレガントなものを探しています:

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

grepの-oは、行を無視して、一致のみを出力します。 wcはそれらを数えることができます:

grep -o 'needle' file | wc -l

これは「針」または「複数針」にも一致します。

単一の単語のみを照合するには、次のいずれかのコマンドを使用します。

grep -ow 'needle' file | wc -l
grep -o '\bneedle\b' file | wc -l
grep -o '\<needle\>' file | wc -l
352
wag

GNU grep(常にLinuxおよびCygwinで、場合によっては他の場所でも)がある場合) grep -oからの出力行をカウントするgrep -o needle | wc -l

Perlを使って、私があなたよりもエレガントだと思う方法がいくつかあります(たとえ fixed になった後でも)。

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ツールのみを使用する場合、可能な場合、1つのアプローチは、grepに渡す前に、単一の一致で入力を行に分割することです。たとえば、単語全体を検索する場合は、最初にWord以外のすべての文字を改行にします。

# 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の2つの出現を検出します)またはneedleneedle内の\bneedle)。

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

上記のsed置換では、\nを使用して改行を意味していることに注意してください。これはパターン部分では標準ですが、移植性のために、\nをバックスラッシュ-改行に置き換えてください。

もし私と同じように "両方;それぞれ正確に1つ"、 (これは実際には「どちらかです; 2回」)それからそれは簡単です:

grep -E "thing1|thing2" -c

そして、出力を確認します2

このアプローチの利点(正確に1回の場合is必要なもの)は、簡単にスケーリングできることです。

5
OJFord

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}'

またはクラスを使用します:[^[:alnum:]]は、すべての非英字を含みます。

3
ripat

これは私の純粋な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
1
Felipe

この例では、ファイル内の合計ではなく、行ごとの発生数のみを出力します。それがあなたの望むことなら、このようなものがうまくいくかもしれません:

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