it-swarm-ru.tech

Подсчитать общее количество вхождений с помощью grep

grep -c полезно для определения того, сколько раз строка встречается в файле, но она считает каждый случай только один раз на строку. Как посчитать несколько вхождений на строку?

Я ищу что-то более элегантное, чем:

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

grep's -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, вот несколько способов, которые я нахожу более элегантными, чем ваш (даже после того, как это исправлено ).

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. Например, если вы ищете целые слова, то сначала превратите каждый символ, отличный от 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

Вот более простое решение с использованием sed и ​​grep, которое работает со строками или даже регулярными выражениями, но не работает в нескольких угловых случаях с привязанными шаблонами (например, он находит два вхождения ^needle или \bneedle in needleneedle).

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

Обратите внимание, что в приведенных выше подстановках sed я использовал \n для обозначения новой строки. Это стандартно в части шаблона, но в тексте замены для переносимости замените обратную косую черту на новую строку \n.

18

Если, как и я, вы на самом деле хотели "оба; каждый ровно один раз", (на самом деле это "либо; дважды"), тогда все просто:

grep -E "thing1|thing2" -c

и проверьте вывод 2.

Преимущество этого подхода (если он точно один раз есть то, что вы хотите) в том, что он легко масштабируется.

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