¿Cómo hacer un partido no codicioso en grep?

Respuestas:

276

Estás buscando una pareja no codiciosa (o perezosa). Para obtener una coincidencia no codiciosa en expresiones regulares, debe usar el modificador ?después del cuantificador. Por ejemplo, puede cambiar .*a .*?.

De forma predeterminada grep, no admite modificadores no codiciosos, pero puede grep -Pusar la sintaxis de Perl.

Mark Byers
fuente
3
eegg: dot all modifier también se conoce como multilínea. Es un modificador que cambia el "." emparejar el comportamiento para incluir nuevas líneas (normalmente no lo hace). No existe tal modificador en grep, pero sí en pcregrep .
A. Wilson
1
Corrección: en la mayoría de los sabores de expresiones regulares que lo admiten, el modo que permite .coincidir con las nuevas líneas se llama DOTALL o modo de una sola línea ; Ruby es el único que lo llama multilínea . En los otros sabores, multilínea es el modo que permite que los anclajes ( ^y $) coincidan en los límites de la línea. Ruby no tiene modo equivalente porque en Ruby siempre funcionan de esa manera.
Alan Moore
55
-Pera completamente nuevo para mí, he estado felizmente trabajando durante años, y solo usando -E... ¡tantos años desperdiciados! - Nota personal: vuelva a leer las páginas Man como algo normal (¡incluso más!), Nunca digiere suficientes interruptores y opciones.
ocodo
29
En algunas plataformas (como Mac OS X) grepno es compatible -P, pero si lo usa egrep, puede usar el .*?patrón para lograr el mismo resultado. egrep -o 'start.*?end' text.html
SaltyNuts
44
Como una extensión al comentario de @SaltyNuts, Mac OS X no es compatible, -Ppero -Ellamaría, egreppor lo tanto, los .*?trabajos sugeridos funcionan bien.
Fredrik Erlandsson
83

En realidad el .*?único funciona en perl. No estoy seguro de cuál sería la sintaxis de expresión regular grep extendida equivalente. Afortunadamente, puede usar la sintaxis de perl con grep, por grep -Plo que funcionaría, pero grep -Ees lo mismo egrepque no funcionaría (sería codicioso).

Ver también: http://blog.vinceliu.com/2008/02/non-greedy-regular-expression-matching.html

John Smith
fuente
9
grep -Pno funciona en GNU grep 2.9 - solo lo probé (no es un error, solo silenciosamente no aplica ?. Intertestly tampoco la clase no, por ejemplo:env|grep '[^\=]*\='
roberto tomás
2
No hay ninguna grep -Popción o pgrepcomando en Darwin / OS X 10.8 Mountain Lion, pero egrepfunciona muy bien.
Steve HHH
2
Hay un pgrepcomando en mi cuadro OS X 10.9, pero es un programa completamente diferente cuyo propósito es "encontrar o señalar procesos por nombre".
Desty
@ robertotomás Respondiendo a un comentario de 6 años aquí, pero ... También pensé esto y luego me di cuenta de que estaba obteniendo múltiples coincidencias no codiciosas. Por ejemplo, en un terminal de color puede ver que `echo" bbbbb "| grep -P 'b. *? b' 'devuelve 2 coincidencias.
zzxyz
12

Mi grep que funciona después de probar cosas en este hilo:

echo "hi how are you " | grep -shoP ".*? "

Solo asegúrate de agregar un espacio a cada una de tus líneas

(El mío fue una búsqueda línea por línea para escupir palabras)

jonz
fuente
3
-shoPnice mnemonic :)
Mariusz
echo "bbbbb" | grep -shoP 'b.*?b'Es un poco una experiencia de aprendizaje. Lo único que me funcionó en términos de pereza explícita también.
zzxyz
12

grep

Para una coincidencia no codiciosa grep, puede usar una clase de caracteres negada. En otras palabras, trate de evitar los comodines.

Por ejemplo, para obtener todos los enlaces a archivos jpeg del contenido de la página, usaría:

grep -o '"[^" ]\+.jpg"'

Para tratar con varias líneas, canalice la entrada xargsprimero. Para rendimiento, uso ripgrep.

kenorb
fuente
3

La respuesta corta es usar la siguiente expresión regular:

(?s)<car .*? model=BMW .*?>.*?</car>
  • (? s): esto hace una coincidencia en varias líneas
  • . *? - coincide con cualquier personaje, varias veces de manera perezosa (coincidencia mínima)

Una respuesta (poco) más complicada es:

(?s)<([a-z\-_0-9]+?) .*? model=BMW .*?>.*?</\1>

Esto hará posible que coincidan car1 y car2 en el siguiente texto

<car1 ... model=BMW ...>
...
...
...
</car1>
<car2 ... model=BMW ...>
...
...
...
</car2>
  • (..) representa un grupo de captura
  • \ 1 en este contexto coincide con el mismo texto que coincidió más recientemente al capturar el grupo número 1
jmc
fuente
1

Lo siento, llego 9 años tarde, pero esto podría funcionar para los espectadores en 2020.

Supongamos que tienes una línea como "Hello my name is Jello". Ahora desea encontrar las palabras que comienzan 'H'y terminan 'o'con cualquier número de caracteres intermedios. Y no queremos líneas, solo queremos palabras. Entonces para eso podemos usar la expresión:

grep "H[^ ]*o" file

Esto devolverá todas las palabras. La forma en que esto funciona es que: permitirá todos los caracteres en lugar del espacio en el medio, de esta manera podemos evitar varias palabras en la misma línea.

Ahora puede reemplazar el carácter de espacio con cualquier otro carácter que desee. Suponga que la línea inicial era "Hello-my-name-is-Jello", entonces puede obtener palabras usando la expresión:

grep "H[^-]*o" file
mr.1n5an_e
fuente
0

Sé que es una publicación un poco muerta, pero acabo de notar que esto funciona. Eliminó tanto la limpieza como la limpieza de mi salida.

> grep -v -e 'clean\-\?up'
> grep --version grep (GNU grep) 2.20
usuario200850
fuente