grep -c
es útil para encontrar cuántas veces ocurre una cadena en un archivo , pero solo cuenta cada aparición una vez por línea. ¿Cómo contar múltiples ocurrencias por línea?
Estoy buscando algo más elegante que:
perl -e "$_ = <>; print scalar ( () = m/needle/g ), "\n""
Comentarios
Answer
grep «s -o
solo generará las coincidencias, ignorando las líneas; wc
puede contarlos:
grep -o "needle" file | wc -l
Esto también coincidirá con «agujas» o «multiagujas».
Para hacer coincidir solo palabras individuales, use uno de los siguientes comandos:
grep -ow "needle" file | wc -l grep -o "\bneedle\b" file | wc -l grep -o "\<needle\>" file | wc -l
Comentarios
- Tenga en cuenta que esto requiere GNU grep (Linux, Cygwin, FreeBSD, OSX).
- @wag ¿Qué magia hace
\b
y\B
¿aquí? - @Geek \ b coincide con un límite de palabra, \ B NO coincide con un límite de palabra. La respuesta anterior sería más correcta si usara \ b en ambos extremos.
- Para un recuento de ocurrencias por línea, combine con la opción grep -n y uniq -c … grep -no ' \ < aguja \ > ' archivo | uniq -c
- @jameswarren
uniq
solo elimina las líneas idénticas adyacentes, debesort
antes de alimentar auniq
si aún no está seguro de que los duplicados siempre serán inmediatamente adyacentes.
Responder
Si tiene GNU grep (siempre en Linux y Cygwin, ocasionalmente en otros lugares), puede contar las líneas de salida de grep -o
: grep -o needle | wc -l
.
Con Perl, aquí hay algunas formas que encuentro más elegantes que las suyas (incluso después de que «s fijo ).
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"
Con solo herramientas POSIX, un enfoque, si es posible, es dividir el ingrese en líneas con una sola coincidencia antes de pasarlo a grep. Por ejemplo, si está buscando palabras completas, primero convierta cada carácter que no sea una palabra en una nueva línea.
# equivalent to grep -ow "needle" | wc -l tr -c "[:alnum:]" "[\n*]" | grep -c "^needle$"
De lo contrario, no hay un comando estándar para hacer esto s un poco particular de procesamiento de texto, por lo que debe recurrir a sed (si «es un masoquista) o 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
Aquí» una solución más simple usando sed
y grep
, que funciona para cadenas o incluso expresiones regulares según el libro, pero falla en algunos casos de esquina con patrones anclados ( p.ej encuentra dos ocurrencias de ^needle
o \bneedle
en needleneedle
).
sed "s/needle/\n&\n/g" | grep -cx "needle"
Tenga en cuenta que en las sustituciones sed anteriores, utilicé \n
para significar una nueva línea. Esto es estándar en la parte del patrón, pero en el texto de reemplazo, por portabilidad, sustituya la barra invertida-nueva línea por \n
.
Respuesta
Si, como yo, en realidad querías «ambos; cada uno exactamente una vez», (esto es en realidad «cualquiera; dos veces») entonces es simple :
grep -E "thing1|thing2" -c
y verifique el resultado 2
.
El beneficio de este enfoque (si exactamente una vez es lo que desea) es que se escale fácilmente.
Comentarios
- I ' No estoy seguro de que ' estés comprobando que ' solo aparece una vez. ¿Todos los ' Lo que busca es que alguna de esas palabras exista al menos una vez.
- Esta debería ser la respuesta aceptada. No es necesario utilizar
wc -l
,grep
tiene una opción incorporada para contar cosas, e incluso se denomina como obvia como-c
para «count»!
Answer
Otro solución usando awk y needle
como separador de campo:
awk -F"^needle | needle | needle$" "{c+=NF-1}END{print c}"
Si desea hacer coincidir needle
seguido de puntuación, cambie el separador de campo en consecuencia, es decir,
awk -F"^needle[ ,.?]|[ ,.?]needle[ ,.?]|[ ,.?]needle$" "{c+=NF-1}END{print c}"
O use la clase: [^[:alnum:]]
para abarcar todos los caracteres no alfabéticos.
Comentarios
- Tenga en cuenta que esto requiere un awk que admita separadores de campos regexp (como GNU awk).
Respuesta
Esta es mi solución bash pura
#!/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
Respuesta
Su ejemplo solo imprime el número de ocurrencias por línea, y no el total en el archivo. Si eso es lo que desea, algo como esto podría funcionar:
perl -nle "$c+=scalar(()=m/needle/g);END{print $c}"
Comentarios
- Usted tienen razón, mi ejemplo solo cuenta las ocurrencias en la primera línea.
grep
, pero para cualquiera que useack
, la respuesta es simplementeack -ch <pattern>
.