¿Puede grep mostrar solo palabras que coinciden con el patrón de búsqueda?

685

¿Hay alguna manera de hacer "palabras" de salida grep a partir de archivos que coincidan con la expresión de búsqueda?

Si quiero encontrar todas las instancias de, digamos, "th" en varios archivos, puedo hacer:

grep "th" *

pero la salida será algo así (negrita es por mí);

algún archivo de texto: el gato se sentó en el tapete  
otro archivo de texto: el rápido zorro marrón  
otro archivo de texto: espero que esto lo explique a fondo 

Lo que quiero que salga, usando la misma búsqueda, es:

the
the
the
this
thoroughly

¿Es esto posible usando grep? ¿O usando otra combinación de herramientas?

Neil Baldwin
fuente
2
La solución de Dan Midwood funciona perfectamente y merece el crédito.
Hakish
¿Hay alguna manera de imprimir esas palabras coincidentes sin cambiar las líneas? ¿Más bien la cadena coincidente debe permanecer en la misma línea?
Linguist

Respuestas:

955

Prueba grep -o

grep -oh "\w*th\w*" *

Editar: coincidencia del comentario de Phil

De los documentos :

-h, --no-filename
    Suppress the prefixing of file names on output. This is the default
    when there is only  one  file  (or only standard input) to search.
-o, --only-matching
    Print  only  the matched (non-empty) parts of a matching line,
    with each such part on a separate output line.
Dan Midwood
fuente
99
@ user181548, la opción grep -o solo funciona para GNU grep. Entonces, si no está utilizando GNU grep, podría no funcionar para usted.
ksinkar
55
@ABB Depende de si desea mostrar el nombre del archivo coincidente o no. No estoy seguro de en qué condiciones se muestra y no se muestra, pero sí sé que cuando utilicé grep en varios directorios, sí mostró la ruta completa del archivo para todos los archivos coincidentes, mientras que con -h solo mostró palabras coincidentes sin ninguna especificación sobre qué archivo es. Entonces, para que coincida con la pregunta original, creo que es necesario en ciertas circunstancias.
LokMac
1
Necesitaba una explicación de lo que "\w*th\w*" *significa, así que pensé en publicar. \wes [_ [: alnum:]], por lo que coincide básicamente con cualquier "palabra" que contenga 'th' (ya \wque no incluye espacio). El * después de la sección citada es un globo para los archivos (es decir, que coinciden con todos los archivos en este directorio)
jeremysprofile
1
\wgeneralmente no es portátil para grep -E; para una portabilidad adecuada, use el nombre de la clase de caracteres POSIX en su [[:alnum:]]lugar (o [_[:alnum:]]si realmente quiere el guión bajo también, o intente grep -Psi su plataforma lo tiene).
tripleee
@ABB Dado el resultado deseado que muestra el OP, ¿ -hes completamente necesario, diría que ...?
El Ronnoco
81

Respuesta segura de distribución cruzada (incluyendo windows minGW?)

grep -h "[[:alpha:]]*th[[:alpha:]]*" 'filename' | tr ' ' '\n' | grep -h "[[:alpha:]]*th[[:alpha:]]*"

Si está utilizando versiones anteriores de grep (como 2.4.2) que no incluye la opción -o. Usa lo anterior. De lo contrario, use la versión más simple para mantener la siguiente.

Respuesta segura de distribución cruzada de Linux

grep -oh "[[:alpha:]]*th[[:alpha:]]*" 'filename'

A resúmenes -oh , las expresiones regulares coinciden con el contenido del archivo (y no con su nombre de archivo), tal como se esperaría que funcionara la expresión regular en vim / etc ... Qué palabra o expresión regular estaría buscando entonces, depende de ¡tú! Mientras permanezca en POSIX y no en la sintaxis de Perl (consulte a continuación)

Más del manual para grep

-o      Print each match, but only the match, not the entire line.
-h      Never print filename headers (i.e. filenames) with output lines.
-w      The expression is searched for as a word (as if surrounded by
         `[[:<:]]' and `[[:>:]]';

La razón por la cual la respuesta original no funciona para todos

El uso de \wvaría de una plataforma a otra, ya que es una sintaxis "perl" extendida. Como tal, la instalación grep que se limita al trabajo con clases de caracteres POSIX usa [[:alpha:]]y no su equivalente perl \w. Vea la página de Wikipedia sobre expresiones regulares para más

En última instancia, la respuesta POSIX anterior será mucho más confiable independientemente de la plataforma (que es la original) para grep

En cuanto al soporte de grep sin la opción -o, el primer grep genera las líneas relevantes, el tr divide los espacios en nuevas líneas, los filtros grep finales solo para las líneas respectivas.

(PD: Sé que la mayoría de las plataformas por ahora, habrían sido parcheadas para \ w ... pero siempre hay aquellas que se quedan atrás)

Crédito por la solución "-o" de la respuesta de @AdamRosenfield

PicoCreator
fuente
1
¿Qué pasa con -o solo trabajando en GNU grep (como mencionó ksinkar en un comentario sobre la respuesta aceptada)?
Brilliand
@Brilliand hmm, estoy teniendo problemas para encontrar una implementación de Linux que no sea compatible con '-o', puedo buscar una solución si sé con qué plataforma comparar.
PicoCreator
@pico La -oopción no está presente en el grep de Windows que se instala con el paquete git (minGW?): "c:\Program Files (x86)\Git\bin\grep" --version grep (GNU grep) 2.4.2
Bruce Peterson
@BrucePeterson He agregado en AdamRosenfield la solución alternativa para -o: Ayúdame a verificar si el git de Windows incluye tr / sed y su versión. Así puedo verificar si esta solución funciona
PicoCreator
@pico: para GIT: GNU sed versión 4.2.1, tr (GNU textutils) 2.0
Bruce Peterson el
46

Es más simple de lo que piensas. Prueba esto:

egrep -wo 'th.[a-z]*' filename.txt #### (Case Sensitive)

egrep -iwo 'th.[a-z]*' filename.txt  ### (Case Insensitive)

Dónde,

 egrep: Grep will work with extended regular expression.
 w    : Matches only word/words instead of substring.
 o    : Display only matched pattern instead of whole line.
 i    : If u want to ignore case sensitivity.
Abhinandan prasad
fuente
2
Esto no parece agregar nada sobre las respuestas existentes de más de 4 años antes.
tripleee
3
@tripleee Encontré que mi enfoque es mejor y simple, así que publiqué esto.
Abhinandan prasad
42

Puede traducir espacios a nuevas líneas y luego grep, por ejemplo:

cat * | tr ' ' '\n' | grep th
Adam Rosenfield
fuente
18
no necesita gato tr '' '\ n' <archivo | grep th. Lento para archivos grandes.
ghostdog74
Esto no funcionó. La salida todavía contenía el nombre del archivo y la línea completa del archivo que contenía la coincidencia. De todos modos, una de las otras soluciones ofrecidas funcionó. Gracias por la entrada sin embargo.
Neil Baldwin
@ ghostdog74: buen punto, aunque si tiene más de un archivo, necesitará usar cat. @Neil Baldwin: ¿estás seguro de que lo escribiste bien? Cuando solo hay un archivo de entrada (stdin en este caso), grep no imprime el nombre del archivo.
Adam Rosenfield
@ Adam - sí, lo siento Adam, funciona con un archivo pero no con varios.
Neil Baldwin
44
@ ghostdog74 si la parte lenta se debe a trque podría hacerlo grepprimero, por trlo que se aplicaría solo a las líneas coincidentes:grep th filename | tr ' ' '\n' | grep th
Carcamano
37

Simplemente awk, no necesita combinación de herramientas.

# awk '{for(i=1;i<=NF;i++){if($i~/^th/){print $i}}}' file
the
the
the
this
thoroughly
ghostdog74
fuente
8
@AjeetGanga bueno, está en el nombre
Daerdemandt
11

comando grep solo para coincidencia y perl

grep -o -P 'th.*? ' filename
Raghu
fuente
3
¿Qué pasa con la visualización de solo el grupo coincidente?
Bishwas Mishra
Esto no funciona solo se encontrará thporque solicitó la repetición más breve posible del comodín.
tripleee
@tripleee: no tendrá ese problema, porque hay un espacio incluido al final de la expresión regular. Sin embargo, perderá palabras que no tengan espacios después de ellas, por ejemplo, al final de las líneas.
Ken Williams
8

No estaba satisfecho con la sintaxis difícil de recordar de awk, pero me gustó la idea de usar una utilidad para hacer esto.

Parece que ack (o ack-grep si usa Ubuntu) puede hacer esto fácilmente:

# ack-grep -ho "\bth.*?\b" *

the
the
the
this
thoroughly

Si omite la bandera -h obtendrá:

# ack-grep -o "\bth.*?\b" *

some-other-text-file
1:the

some-text-file
1:the
the

yet-another-text-file
1:this
thoroughly

Como --outputbeneficio adicional, puede usar el indicador para hacer esto en búsquedas más complejas con la sintaxis más fácil que he encontrado:

# echo "bug: 1, id: 5, time: 12/27/2010" > test-file
# ack-grep -ho "bug: (\d*), id: (\d*), time: (.*)" --output '$1, $2, $3' test-file

1, 5, 12/27/2010
Galán
fuente
8
cat *-text-file | grep -Eio "th[a-z]+"
Murmurando Mac
fuente
2
o simplemente grep -Eio "th [az] +" nombre de archivo
Shayan
3
Quizás vea también Uso inútil de cat?
tripleee
4

Para buscar todas las palabras que comienzan con "icono-", el siguiente comando funciona perfectamente. Estoy usando Ack aquí, que es similar a grep pero con mejores opciones y buen formato.

ack -oh --type=html "\w*icon-\w*" | sort | uniq
Sandeep
fuente
3

También puedes probar pcregrep . También hay una -wopción en grep , pero en algunos casos no funciona como se esperaba.

De Wikipedia :

cat fruitlist.txt
apple
apples
pineapple
apple-
apple-fruit
fruit-apple

grep -w apple fruitlist.txt
apple
apple-
apple-fruit
fruit-apple
Maciek Sawicki
fuente
3

Tuve un problema similar, buscando grep / pattern regex y el "patrón coincidente encontrado" como salida.

Al final usé egrep (la misma expresión regular en grep -e o -G no me dio el mismo resultado que egrep) con la opción -o

Entonces, creo que podría ser algo similar a (NO soy un maestro de expresiones regulares):

egrep -o "the*|this{1}|thoroughly{1}" filename
keebOo
fuente
Los {1}cuantificadores inútiles deberían descartarse. O si quieres ser consistente, t{1}h{1}e{1}etc.
tripleee
¿Se puede imprimir con la misma línea?
吴毅 凡
-1

Podría canalizar su salida grep a Perl de esta manera:

grep "th" * | perl -n -e'while(/(\w*th\w*)/g) {print "$1\n"}'

fuente
99
eso no dará el resultado correcto. Además, si usa Perl, no necesita usar grep. haz todo en Perl.
ghostdog74
Gracias por señalar el error, ghostdog74. Lo he cambiado para imprimir todas las palabras en la línea, no solo la primera.
como dije, grep no es necesario. perl -n -e'while (/ (\ s + th \ w *) / g) {print "$ 1 \ n"} 'file
ghostdog74
77
Depende de usted. Solo estoy ilustrando un punto. Si no es necesario, no lo hagas. ese extra "|" te costará un proceso más.
ghostdog74
1
En Perl 5.10 o posterior: perl -nE '@a = / (regexp) / ig; say join "\ n", @a '
Profesor Photon
-1
$ grep -w

Extracto de la página de manual de grep:

-w: selecciona solo aquellas líneas que contienen coincidencias que forman palabras completas. La prueba es que la subcadena coincidente debe estar al principio de la línea o precedida por un carácter constituyente que no sea una palabra.

pl1nk
fuente
1
Eso todavía imprimirá toda la línea que contiene la coincidencia. Restringe la coincidencia real para que theya no coincida, por ejemplo, "estos" o "bañarse".
tripleee
-6

ripgrep

Aquí están los ejemplos usando ripgrep:

rg -o "(\w+)?th(\w+)?"

Coincidirá con todas las palabras que coincidan th.

kenorb
fuente