Historia de Bash globbing

11

¿Hay alguna razón histórica por la que Bash "globbing" y las expresiones regulares no sean idénticas? Por ejemplo, creo que en Bash [1-2]*coincide todo lo que comienza con un 1 o un 2 seguido de cualquier otra cosa, mientras que como expresión regular [1-2]*solo coincidiría con una secuencia de 1s y 2s. Mis secuencias de comandos Bash y REGEX son bastante débiles y regularmente me encuentro con problemas asociados con estas diferencias, lo que me hizo sentir curiosidad por saber por qué son diferentes.

Fuerte
fuente
3
¿Considerarías hacerlo en rm -- ^[^.].*\.txt$lugar de rm -- *.txt?
Stéphane Chazelas
1
Gran parte de sus Q se tocan en este hilo de lwn: lwn.net/Articles/96687
slm
Hay comandos que operan en los nombres de archivo y toman expresiones regulares. Por ejemplo, find, find . -regex ".*\.txt$" | xargs rm --o renamepara renombrar archivos (es sedpara nombres de archivos), tenga cuidado con algunos sistemas que tienen una diferente rename.
ctrl-alt-delor
@richard, mi ^[^.].*\.txt$fue tener en cuenta el hecho de ignorar los archivos de puntos. Tenga en cuenta que el -regexes un extensiones de GNU, algunas conchas como ksh93 o zsh pueden incorporar expresiones regulares en sus pegotes (pruebe por ejemplo: ksh93 -c 'echo ~(E:^[^.].*\.txt$)')
Stéphane Chazelas
2
Ese golpe sigue la práctica existente con tanto cuidado mientras evita cambios y extensiones irreconciliablemente incompatibles es una de sus mayores fortalezas.
ormaaj

Respuestas:

12

bashinicialmente fue diseñado a finales de los 80 como un clon parcial de kshalgunas características interactivas de csh / tcsh.

Los orígenes del globbing se deben encontrar en los caparazones anteriores sobre los que se basa.

kshen sí es una extensión del shell Bourne. El shell Bourne en sí (lanzado por primera vez en 1979 en Unix V7) fue una implementación limpia desde cero, pero no se apartó por completo del shell Thompson (el shell de V1 -> V6) e incorporó características del shell Mashey.

En particular, los argumentos de comando todavía estaban separados por espacios en blanco, |ahora era el nuevo operador de tubería pero ^todavía era compatible como una alternativa (y también explica por qué lo hace [!a-z]y no [^a-z]), $1seguía siendo el primer argumento para un script y la barra diagonal inversa seguía siendo el carácter de escape . Muchos de los operadores regexp ( ^\|$) tienen un significado especial propio en el shell.

El shell Thompson se basó en una utilidad externa para el globbing. Cuando se shencuentra sin comillas *, [o ?s en el comando, ejecutaría el comando glob.

rm *.txt

terminaría ejecutando glob como:

["glob", "rm", "*.txt"]

y glob terminaría ejecutándose rmcon la lista de archivos que coinciden con ese patrón.

grep a.\*b *.txt

correría globcomo:

["glob", "grep", "a.\252b", "*.txt"]

Lo *anterior se ha citado estableciendo el 8vo bit en ese carácter, evitando que lo globtrate como un comodín. globluego eliminaría ese bit antes de llamar grep.

Para hacer el equivalente con regexps, eso habría sido:

regexp rm '\.txt$'

O:

regexp rm '^[^.].*\.txt$'

para excluir archivos de puntos.

La necesidad de escapar de los operadores, ya que funcionan como caracteres especiales de shell, el hecho de que ., común en los nombres de archivo es un operador regexp, hace que no sea muy apropiado hacer coincidir los nombres de archivo y complicado para un principiante. En la mayoría de los casos, todo lo que necesita son comodines que puedan reemplazar uno ( ?) o cualquier número ( *) de caracteres.

Ahora, diferentes shells agregaron diferentes operadores de globbing. Hoy en día, los globos ksh y zsh (y hasta cierto punto, bash -O extglobque implementan un subconjunto de globos ksh) son funcionalmente equivalentes a expresiones regulares con una sintaxis que es menos engorrosa de usar con los nombres de archivo y la sintaxis de shell actual. Por ejemplo, en zsh(con la extensión Extendedglob), puede hacer:

echo a#.txt

si desea (poco probable) que coincida con los nombres de archivo que consisten en secuencias aseguidas de .txt. Más fácil que echo (^a*\.txt$)(aquí usando llaves como una forma de aislar a los operadores de expresiones regulares de los operadores de shell que podrían haber sido una forma en que los shells podrían lidiar con eso).

echo (foo|bar|<1-20>).(#i)mpg

Para archivos mpg (sin distinción entre mayúsculas y minúsculas) cuyo nombre base es foo, bar o un número decimal del 1 al 20 ...

ksh93ahora también puede incorporar expresiones regulares (básicas, extendidas, perl-like o "aumentadas") en sus globos (aunque es bastante defectuoso) e incluso proporciona una herramienta para convertir entre glob y regexp ( printf %R, printf %P):

echo ~(Ei:.*\.txt)

al partido (no oculta) txt con E XTended expresiones regulares, entre mayúsculas i nsensitively.

Stéphane Chazelas
fuente
Genial redacción! En realidad no puede usar ~(opt:pat)ninguna de las opciones en mayúscula. Tal vez print -r -- ~(Ei).*\.txt$. Poner el patrón dentro parece ser útil solo para evitar tener que activar una opción y luego desactivarla durante parte de un patrón. Curiosamente, puedes mezclar y combinar múltiples lenguajes de patrones dentro del mismo globo. ~(Ki)*.~(E)txt$es equivalente. (Al final, todo se convierte en expresiones regulares y se pasa internamente al motor de expresiones regulares de libast).
ormaaj
@ormaaj, ~(Ei:.*\.txt)funciona para mí incluso con versiones de 15 años como ksh93 o +.
Stéphane Chazelas
También funciona con uno de mis binarios de prueba guardados (2014-12-24), pero recuerdo haber tenido problemas con eso. Las cosas siempre se rompieron al azar y se arreglaron nuevamente entre cada versión cuando ksh aún se desarrollaba comercialmente. Recuerdo que el código de coincidencia de patrones es una de las áreas frágiles.
ormaaj
@ormaaj, uno diferente entre ~(E)xy ~(E:x)es que este último está anclado (coincide xsolo mientras que el anterior coincide con cualquier cosa que contenga x), que puede ser el tipo de problema con el que se encontró (usar ~(-lr)~(E:x)para eliminar el anclaje, ~(E-lr:x)no funcionará). En cualquier caso, estoy de acuerdo en que es bastante defectuoso, incluso en la última versión.
Stéphane Chazelas
9

Kleene introdujo los idiomas regulares en 1956. El documento seminal no tenía la notación moderna completa para las expresiones regulares, pero sí introdujo la "estrella de Kleen": que A*significa "cualquier número de repeticiones de A". En la próxima década, surgieron algunas notaciones más o menos estándar, en particular .para un carácter arbitrario y ?para significar que el carácter anterior es opcional.

La notación global de Bash se deriva del globcomando introducido en Unix v1 en 1971. En ese momento, la aplicación global fue realizada por un programa separado; Más tarde se trasladó a la cáscara. El globcomando inicial tiene ?que significar "cualquier carácter" y *significa "cualquier secuencia de caracteres". No sé por qué los personajes fueron elegidos; ?es bastante intuitivo y *puede haberse inspirado en el de las expresiones regulares.

Globbing no pretendía ser tan general como las expresiones regulares, y las expresiones regulares no estaban muy extendidas en ese momento, por lo que no hubo un llamado a unificar los conceptos. Desde el principio, había incompatibilidades sintácticas, con ?, .y *significa cosas diferentes en patrones de nombre de archivo y en las expresiones regulares.

Los proyectiles modernos, como bash, se expanden en los patrones globales, pero fue una evolución gradual que mantuvo la compatibilidad con versiones anteriores. Ksh88 (la versión de 1988 del shell Korn ) introdujo una sintaxis extendida para patrones de shell, que no podía ser la misma sintaxis que las expresiones regulares habituales, pero estaba fuertemente inspirada por ella: *(PATTERN)significar cualquier número de repeticiones de PATTERN, @(PATTERN1|PATTERN2)significar " PATTERN1o PATTERN2", etc.

Las versiones modernas de bash (desde 2.02) admiten los patrones extendidos de ksh88, si publica shopt -s extglobprimero.

Gilles 'SO- deja de ser malvado'
fuente
¿Bash nunca ha admitido extglobs? Hasta donde yo sé, Bash, zsh y {pd, m} ksh han admitido exactamente los mismos globs que se documentan en el manual de ksh88 desde los primeros días. Ksh hasta el día de hoy ni siquiera tiene una opción para deshabilitar los cuantificadores glob "extendidos", y ksh93 es el único del grupo que tiene extensiones más allá de lo que tenía ksh88.
ormaaj
2
@ormaaj Ksh88 extendió los globos y la extglobopción se introdujo en bash 2.02 en algún lugar alrededor de 1998. Zsh adquirió ksh_globen la serie 3.1 en algún lugar al mismo tiempo. Zsh tiene muchas extensiones propias (algunas requieren la extended_globopción).
Gilles 'SO- deja de ser malvado'
Veo. Entonces, en realidad fue lo suficientemente tarde como para justificar la necesidad de una opción. (Creo que la desactivación predeterminada es bastante inútil en estos días, pero interesante.)
ormaaj
1
@ormaaj, tenga en cuenta que bash, a diferencia de ksh, extglob hace que bash no sea compatible con POSIX porque no está deshabilitado en las variables. En ksh, se var='@(*)'; echo $varexpande a todos los nombres de archivo en el directorio actual que comienzan @(y terminan )como POSIX lo requiere, mientras que en bash -O extglobél se expande a todos los archivos. (aún así, uno puede considerar que el comportamiento bash tiene más sentido aquí (y el comportamiento ksh es bastante doloroso cuando desea tener patrones en las variables)). Esa sintaxis global es muy incómoda debido a eso (compatibilidad POSIX / Bourne). Comparar con zsh globos extendidos.
Stéphane Chazelas
@ StéphaneChazelas Eso es todo cierto, y me gusta cómo ksh es algo inteligente al respecto. Sin embargo, rara vez entra en juego a menos que en realidad esté limitado a POSIX. Con casi todos los usos de la división de palabras reemplazados por mejores características, y el almacenamiento de patrones en variables es una molestia extrema de todos modos ya que tiene que vaciar IFS, deshabilite la expansión de llaves en todas partes menos bash. Creo que todavía es imposible estar completamente seguro con los patrones almacenados. Este viejo problema de escape nunca se resolvió realmente, por ejemplo.
ormaaj
1

Razón histórica: SI. Referencia:
http://en.wikipedia.org/wiki/Glob_(programming)#Origin

Solo para mostrar la divergencia, aquí hay un ejemplo bueno y fácil: a*

  • globbing de shell: el significado es, primero el carácter es ay luego lo que sea (a, ab, abca ...)
  • regex: significado es, cero o más repeticiones de caracteres a(a, aa, aaa ...)

Estoy de acuerdo en que esta discrepancia en el significado es muy confusa para los nuevos usuarios.

El globbing es quizás más fácil de entender para los recién llegados, pero también es una construcción menos poderosa.

fgeorgatos
fuente