Grep: El asterisco (*) no siempre funciona

Grep: El asterisco (*) no siempre funciona

Si recojo un documento que contiene lo siguiente:

ThisExampleString

...para la expresión This*Stringo *String, no se devuelve nada. Sin embargo, This*devuelve la línea anterior como se esperaba.

No importa si la expresión está entre comillas.

Pensé que el asterisco indicaba algún número de caracteres desconocidos. ¿Por qué sólo funciona si está al principio de la expresión? Si este es el comportamiento previsto, ¿qué uso en lugar de las expresiones This*Stringy *String?

Respuesta1

un asterisco enexpresiones regularessignifica "coincidir con el elemento anterior 0 o más veces".

En su caso particular con grep 'This*String' file.txt, está tratando de decir "oye, grep, empareja la palabra Thi, seguida de scero minúsculas o más veces, seguida de la palabra String". Las minúsculas sno se encuentran en ninguna parte Example, por lo que grep las ignora ThisExampleString.

En el caso de grep '*String' file.txt, estás diciendo "grep, haz coincidir la cadena vacía (literalmente nada) que precede a la palabra String". Por supuesto, no es así como ThisExampleStringse supone que debe leerse. (Hayotros posibles significados(puedes probar esto con y sin la -Ebandera, pero ninguno de los significados se parece en nada a lo que realmente quieres aquí).

Sabiendo que eso .significa "cualquier carácter", podríamos hacer esto: grep 'This.*String' file.txt. Ahora el comando grep lo leerá correctamente: Thisseguido de cualquier carácter (considérelo como una selección de caracteres ASCII) repetido cualquier cantidad de veces, seguido de String.

Respuesta2

El *metacarácter en BRE 1 , ERE 1 y PCRE 1 coincide con 0 o más apariciones del patrón agrupado previamente (si un patrón agrupado precede al *metacarácter), 0 o más apariciones de la clase de carácter anterior (si una clase de carácter es precede al *metacarácter) o 0 o más apariciones del carácter anterior (si ni un patrón agrupado ni una clase de carácter preceden al *metacarácter);

Esto significa que en el This*Stringpatrón, al *no estar precedido el metacarácter ni por un patrón agrupado ni por una clase de carácter, el *metacarácter coincide con 0 o más apariciones del carácter anterior (en este caso el scarácter):

% cat infile               
ThisExampleString
ThisString
ThissString
% grep 'This*String' infile
ThisString
ThissString

Para hacer coincidir 0 o más apariciones de cualquier carácter, desea hacer coincidir 0 o más apariciones del .metacarácter, que coincide con cualquier carácter:

% cat infile               
ThisExampleString
% grep 'This.*String' infile
ThisExampleString

El *metacarácter en BRE y ERE siempre es "codicioso", es decir, coincidirá con la coincidencia más larga:

% cat infile
ThisExampleStringIsAString
% grep -o 'This.*String' infile
ThisExampleStringIsAString

Puede que este no sea el comportamiento deseado; en caso de que no sea así, puede activar grepel motor PCRE de (usando la -Popción) y agregar el ?metacarácter, que cuando se coloca después de los *metacaracteres +y tiene el efecto de cambiar su avidez:

% cat infile
ThisExampleStringIsAString
% grep -Po 'This.*?String' infile
ThisExampleString

1: Expresiones regulares básicas, expresiones regulares extendidas y expresiones regulares compatibles con Perl

Respuesta3

Una de las explicaciones encontradas aquí.enlace:

El asterisco " *" no significa lo mismo en expresiones regulares que en comodines; es un modificador que se aplica al carácter único anterior o a una expresión como [0-9]. Un asterisco coincide con cero o más de lo que le precede. Por lo tanto, [A-Z]*coincide con cualquier número de letras mayúsculas, incluida ninguna, mientras [A-Z][A-Z]*coincide con una o más letras mayúsculas.

Respuesta4

*tiene un significado especial tanto como conchaglobocarácter ("comodín") y como expresión regularmetacarácter. Debes tener ambos en cuenta, aunque sicitasu expresión regular, entonces puede evitar que el caparazón la trate de manera especial y asegurarse de que la pase sin cambios agrep. A pesar dealgo así comoSimilar conceptualmente, lo que *significa para el caparazón es bastante diferente de lo que significa para grep.

Primeroel shell lo trata *como un comodín.

Usted dijo:

No importa si la expresión está entre comillas.

Eso depende de qué archivos existan en cualquier directorio en el que se encuentre cuando ejecute el comando. Para los patrones que contienen el separador de directorio /, puede depender de qué archivos existen en todo el sistema. Tu siempre deberiascitaexpresiones regulares para grep--ycomillas simplessuelen ser los mejores--a menos queestás seguro de que estás bien con elnueve tipos de transformaciones potencialmente sorprendentesel caparazón funciona de otra maneraantesejecutando el grepcomando.

Cuando el shell encuentra un *carácter que no escitado, significa "cero o más de cualquier carácter" yreemplaza la palabra que lo contienecon una lista de nombres de archivos que coinciden con el patrón. (Los nombres de archivo que comienzan con .están excluidos, a menos que su patrón comience con. ohas configurado tu shell para incluirlos de todos modos.) Esto se conoce comoglobo--y también por los nombresexpansión de nombre de archivoyexpansión del nombre de ruta.

El efecto con grepnormalmente será que el primer nombre de archivo coincidente se toma como expresión regular, incluso si sería bastante obvio para un lector humano que esnopensado como una expresión regular, mientras que todos los demás nombres de archivos enumerados automáticamente desde su global se toman como archivosadentrodonde buscar coincidencias. (No ve la lista; se pasa de forma opaca a grep.) Prácticamente no desea que esto suceda.

La razón por la que esto esa vecesno es un problema, y ​​en su caso particular, al menoshasta ahora, no fue... es que *se quedará solosi todo lo siguiente es cierto:

  1. HabíaNoarchivos cuyos nombres coinciden. ...Oha desactivado el globbing en su shell, generalmente con set -fo su equivalente set -o noglob. Pero esto es poco común y probablemente sabrás que lo hiciste.

  2. Está utilizando un shell cuyo comportamiento predeterminado es dejarlo *solo cuando no hay nombres de archivos coincidentes. Este es el caso en Bash, que estásprobablementeusando, pero no en todos los shells estilo Bourne. (El comportamiento predeterminado en el popular shell Zsh, por ejemplo, es que los globos(a)expandir o(b)producir un error.)...Oha cambiado este comportamiento de su shell; la forma en que se hace varía según el shell.

  3. Usted no tienede lo contrariole dijo a su caparazón que permitiera reemplazar los globos connadacuando no hay archivos coincidentes, ni fallar con un mensaje de error en esta situación. En Bash eso se habría hecho habilitando el nullglobofailglob opción de shell, respectivamente.

A veces puedes confiar en los números 2 y 3, pero rara vez puedes confiar en el número 1. Un grepcomando con un patrón sin comillas que funciona ahora puede dejar de funcionar cuando tiene archivos diferentes o cuando lo ejecuta desde un lugar diferente.Cita tu expresión regular y el problema desaparecerá.

Entoncesel grepcomando lo trata *como un cuantificador.

Las otras respuestas, como aquellaspor Sergiy Kolodyazhnyypor kos- También aborde este aspecto de esta pregunta, de maneras algo diferentes. Así que animo a aquellos que aún no los han leído a que lo hagan, ya sea antes o después de leer el resto de esta respuesta.

Suponiendo que *llegue a grep, lo que las citas deberían garantizar, grepentonces se entiende que significa queel elemento que lo precedepuede ocurrir cualquier cantidad de veces, en lugar de tener que ocurrir exactamente una vez. Todavía podría ocurrir una vez. O puede que no esté presente en absoluto. O podría repetirse. Texto que encaja concualquierde esas posibilidades serán igualadas.

¿Qué quiero decir con "artículo"?

  • un solopersonaje. Dado que bcoincide con un literal b, b*coincide con cero o más bs, por lo tanto ab*ccoincide con ac,,,, etc.abcabbcabbbc

    Del mismo modo, desde.coincide con cualquier personaje, .*coincide con cero o más caracteres1, por lo tanto a.*ccoincide con ac, akc, ahjglhdfjkdlgjdfkshlgc, par acccccchjckhcc, etc.O

  • Aclase de personaje. Dado que coincide [xy]con xo y, coincide con [xy]*cero o más caracteres donde cada uno es xo y, por lo tanto p[xy]*qcoincide con pq,,,,,,,,,,, etc.pxqpyqpxxqpxyqpyxqpyyqpxxxqpxxyq

    Esto también se aplica aformas taquigráficasde clases de personajes como \w, \W, \sy \S. Dado que \wcoincide con cualquier carácter de palabra, \w*coincide con cero o más caracteres de palabra.O

  • Agrupo. Dado que coincide \(bar\)con cero o más s, coincide con , , , etc.bar\(bar\)*barfoo\(bar\)*bazfoobazfoobarbazfoobarbarbazfoobarbarbarbaz

    Con las opciones -Eo -P, greptrata su expresión regular como unaANTES DEoPCRErespectivamente, en lugar de comoBRE, y luego los grupos están rodeados por ( )en lugar de \( \), por lo que usarías (bar)en lugar de \(bar\)y foo(bar)bazen lugar de foo\(bar\)baz.

man grepofrece una explicación razonablemente accesible de la sintaxis BRE y ERE al final, además de enumerar todas las opciones de línea de comandos grepaceptadas al principio. Recomiendo esa página del manual como recurso, y tambiénla documentación de GNU Grepyeste tutorial/sitio de referencia(que he vinculado a varias páginas arriba).

Para probar y aprender grep, recomiendo llamarlo con un patrón pero sin nombre de archivo. Luego recibe información de su terminal. Introduzca líneas; las líneas que se le devuelven son las que contenían texto que coincidía con su patrón. Para salir, presione Ctrl+ Dal comienzo de una línea, lo que indica el final de la entrada. (O puede presionar Ctrl+ Ccomo ocurre con la mayoría de los programas de línea de comandos). Por ejemplo:

grep 'This.*String'

Si usa la --colorbandera, grepresaltará el específicopartesde sus líneas que coincidieron con su expresión regular, lo cual es muy útil tanto para descubrir qué hace una expresión regular como para encontrar lo que busca una vez que lo hace. De forma predeterminada, los usuarios de Ubuntu tienen un alias de Bash que hace grep --color=autoque se ejecute (lo cual es suficiente para este propósito) cuando lo ejecuta grepdesde la línea de comandos, por lo que probablemente ni siquiera necesite pasarlo --colormanualmente.

1 Por lo tanto, .*en una expresión regular significa lo que *significa en un globo de shell. Sin embargo, la diferencia es que grepimprime automáticamente líneas que contienen su coincidencia.en cualquier lugaren ellos, por lo que normalmente no es necesario tenerlo .*al principio o al final de una expresión regular.

información relacionada