Відповіді:
grep's -o
видаватиме лише сірники, ігноруючи рядки; wc
може їх порахувати:
grep -o 'needle' file | wc -l
Це також відповідатиме «голки» або «багатоноші».
Лише окремі слова:
grep -o '\bneedle\B' file | wc -l
# or:
grep -o '\<needle\>' file | wc -l
\b
і \B
робить тут?
uniq
видаляє лише сусідні однакові лінії, до sort
чого потрібно, uniq
якщо ви не впевнені, що дублікати завжди будуть негайно сусідніми.
Якщо у вас є 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, один підхід, якщо це можливо, полягає в тому, щоб розділити вхід на рядки за допомогою однієї відповідності перед тим, як передати його в грепп. Наприклад, якщо ви шукаєте цілі слова, то спочатку перетворіть кожен несловесний символ у новий рядок.
# 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
в needleneedle
).
sed 's/needle/\n&\n/g' | grep -cx 'needle'
Зауважимо, що в підстановці сед, наведених вище, я \n
мав на увазі новий рядок. Це є стандартним у частині шаблону, але в тексті, що замінює, для портативності, підміняйте звороту косу рису на новий рядок \n
.
Якщо, як я, ви насправді хотіли "обох; кожен рівно один раз", (це насправді "або два рази"), то це просто:
grep -E "thing1|thing2" -c
і перевірити вихід 2
.
Перевага такого підходу (якщо саме один раз є те, що ви хочете) полягає в тому, що він легко масштабується.
Ще одне рішення, що використовує 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:]]
щоб охопити всі символи, що не належать до альфа.
У вашому прикладі виводиться лише кількість подій на рядок, а не загальна кількість у файлі. Якщо ви цього хочете, щось подібне може спрацювати:
perl -nle '$c+=scalar(()=m/needle/g);END{print $c}'
grep
, що вказано, але для тих, хто використовуєack
, відповідь простаack -ch <pattern>
.