Odpowiedzi:
grep wypisuje -o
tylko dopasowania, ignorując linie; wc
mogę je policzyć:
grep -o 'needle' file | wc -l
Będzie to również pasować do „igieł” lub „igiełek”.
Tylko pojedyncze słowa:
grep -o '\bneedle\B' file | wc -l
# or:
grep -o '\<needle\>' file | wc -l
\b
i \B
robi tutaj?
uniq
usuwa tylko sąsiadujące identyczne linie, musisz to zrobić sort
przed karmieniem, uniq
jeśli nie jesteś już pewien, że duplikaty zawsze będą bezpośrednio sąsiadować.
Jeśli masz GNU grep (zawsze na Linux i Cygwin, czasami gdzie indziej), można liczyć linie wyjściowe zgrep -o
: grep -o needle | wc -l
.
W Perlu jest kilka sposobów, które uważam za bardziej eleganckie niż twoje (nawet po ich naprawieniu ).
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'
W przypadku tylko narzędzi POSIX, jednym z podejść, jeśli to możliwe, jest podzielenie danych wejściowych na linie za pomocą jednego dopasowania przed przekazaniem go do grep. Na przykład, jeśli szukasz całych słów, najpierw zamień każdy znak niebędący znakiem na nowy wiersz.
# equivalent to grep -ow 'needle' | wc -l
tr -c '[:alnum:]' '[\n*]' | grep -c '^needle$'
W przeciwnym razie nie ma standardowego polecenia do wykonania tego konkretnego przetwarzania tekstu, więc musisz przejść do sed (jeśli jesteś masochistą) lub 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
Oto prostsze rozwiązanie wykorzystujące sed
a grep
, który pracuje dla ciągów lub nawet by-the-book wyrażeń regularnych, ale nie powiedzie się w ciągu kilku przypadkach narożnych z zakotwiczonych wzorów (np znajdzie dwa wystąpienia ^needle
lub \bneedle
w needleneedle
).
sed 's/needle/\n&\n/g' | grep -cx 'needle'
Zauważ, że w powyższych podstawieniach sed miałem \n
na myśli nową linię. Jest to standardowe w części wzoru, ale w tekście zastępczym, dla przenośności, zamień na odwrotny ukośnik-nowa linia \n
.
Jeśli, tak jak ja, tak naprawdę chciałeś „oba; każde dokładnie raz” (to właściwie „albo; dwa razy”), to jest to proste:
grep -E "thing1|thing2" -c
i sprawdź dane wyjściowe 2
.
Zaletą tego podejścia (jeśli dokładnie raz jest to, czego chcesz) jest to, że łatwo się skaluje.
Inne rozwiązanie wykorzystujące awk i needle
jako separator pól:
awk -F'^needle | needle | needle$' '{c+=NF-1}END{print c}'
Jeśli chcesz dopasować, needle
a następnie interpunkcję, zmień odpowiednio separator pól, tj
awk -F'^needle[ ,.?]|[ ,.?]needle[ ,.?]|[ ,.?]needle$' '{c+=NF-1}END{print c}'
Lub użyj klasy [^[:alnum:]]
:, aby objąć wszystkie znaki inne niż alfa.
Twój przykład wypisuje tylko liczbę wystąpień na linię, a nie całkowitą liczbę w pliku. Jeśli tego właśnie chcesz, coś takiego może działać:
perl -nle '$c+=scalar(()=m/needle/g);END{print $c}'
grep
jest określony, ale dla każdegoack
, kto używa , odpowiedź jest prostaack -ch <pattern>
.