¿El comodín Bash star * siempre produce una lista ordenada (ascendente)?

53

Tengo un directorio lleno de archivos con nombres como logXXdonde XX es un número hexadecimal en mayúscula de dos caracteres, con relleno de cero, como:

log00
log01
log02
...
log0A
log0B
log0C
...
log4E
log4F
log50
...

En general, habrá menos de 20 o 30 archivos en total. La fecha y hora en mi sistema en particular no es algo en lo que se pueda confiar (un sistema integrado sin fuentes de tiempo confiables NTP o GPS). Sin embargo, los nombres de los archivos se incrementarán de manera confiable como se muestra arriba.

Deseo revisar greptodos los archivos para la entrada de registro más reciente de un tipo determinado, esperaba catreunir los archivos juntos, como ...

cat /tmp/logs/log* | grep 'WARNING 07 -' | tail -n1

Sin embargo, se me ocurrió que diferentes versiones de bashor sho zshetc. podrían tener diferentes ideas sobre cómo *se expande.

La man bashpágina no dice si la expansión de *sería una lista alfabética definitivamente ascendente de nombres de archivos coincidentes. Parece estar ascendiendo cada vez que lo he probado en todos los sistemas que tengo disponibles, pero ¿es un comportamiento DEFINIDO o solo una implementación específica?

En otras palabras, ¿puedo confiar absolutamente en cat /tmp/logs/log*concatenar todos mis archivos de registro juntos en orden alfabético?

Wossname
fuente
1
@ADDB El orden de clasificación predeterminado para sortes el mismo que para el shell cuando se expande un patrón global de nombre de archivo.
Kusalananda
99
Esa es una práctica terrible para nombrar archivos. ¿Por qué comienzas tu carrera con log (0) = - infty?
EP
14
@EP Nuestro sistema de archivos es un complejo hiper-toroide de 7 dimensiones con numeración surrealista de los inodos. Fue protegido por una oscura rama de busybox y ahora estamos atrapados con eso :)
Wossname
1
Puede evitar catcon grep -h pattern /tmp/logs/log*para suprimir los nombres de archivo anteriores a las coincidencias. (Al menos con GNU grep, no verifiqué POSIX o busybox.)
Peter Cordes
1
@Kusalananda Has oído hablar del uso inútil cat, este es un uso inútil del sort
gato

Respuestas:

52

En todos los depósitos, los globos se ordenan por defecto. Ya fueron ayudados por el /etc/globayudante llamado por el caparazón de Ken Thompson para expandir los globos en la primera versión de Unix a principios de los años 70 (y que dieron su nombre a los globos).

Para sh, POSIX requiere que se ordenen por strcoll(), es decir, utilizando el orden de clasificación en la configuración regional del usuario, como lsaunque algunos todavía lo hacen a través de strcmp(), que se basa solo en valores de bytes.

$ dash -c 'echo *'
Log01B log-0D log00 log01 log02 log0A log0B log0C log4E log4F log50 log log lóg01
$ bash -c 'echo *'
log log log00 log01 lóg01 Log01B log02 log0A log0B log0C log-0D log4E log4F log50
$ zsh -c 'echo *'
log log log00 log01 lóg01 Log01B log02 log0A log0B log0C log-0D log4E log4F log50
$ ls
log  log  log00  log01  lóg01  Log01B  log02  log0A  log0B  log0C  log-0D  log4E  log4F  log50
$ ls | sort
log
log
log00
log01
lóg01
Log01B
log02
log0A
log0B
log0C
log-0D
log4E
log4F
log50

Puede observar más arriba que para aquellos shells que hacen la clasificación basada en la configuración regional, aquí en un sistema GNU con una en_GB.UTF-8configuración regional, los -nombres en los archivos se ignoran para la clasificación (la mayoría de los caracteres de puntuación lo harían). El óse clasifica de una manera más esperada (al menos para los británicos), y el caso se ignora (excepto cuando se trata de decidir los lazos).

Sin embargo, notará algunas inconsistencias para log① log②. Esto se debe a que el orden de clasificación de ① y ② no está definido en las configuraciones regionales de GNU (actualmente; con suerte, se solucionará algún día). Clasifican lo mismo, por lo que obtienes resultados aleatorios.

Cambiar la configuración regional afectará el orden de clasificación. Puede establecer la configuración regional en C para obtener un strcmp()tipo similar:

$ bash -c 'echo *'
log log log00 log01 lóg01 Log01B log02 log0.2 log0A log0B log0C log-0D log4E log4F log50
$ bash -c 'LC_ALL=C; echo *'
Log01B log-0D log0.2 log00 log01 log02 log0A log0B log0C log4E log4F log50 log log lóg01

Tenga en cuenta que algunas configuraciones regionales pueden causar algunas confusiones incluso para las cadenas all-alnum all-ASCII. Al igual que los checos (al menos en los sistemas GNU), ¿dónde chhay un elemento de clasificación que se ordena después de h:

$ LC_ALL=cs_CZ.UTF-8 bash -c 'echo *'
log0Ah log0Bh log0Dh log0Ch

O, como señaló @ninjalj, incluso los más extraños en los locales húngaros:

$ LC_ALL=hu_HU.UTF-8 bash -c 'echo *'
logX LOGx LOGX logZ LOGz LOGZ logY LOGY LOGy

En zsh, puede elegir la clasificación con calificadores globales . Por ejemplo:

echo *(om) # to sort by modification time
echo *(oL) # to sort by size
echo *(On) # for a *reverse* sort by name
echo *(o+myfunction) # sort using a user-defined function
echo *(N)  # to NOT sort
echo *(n)  # sort by name, but numerically, and so on.

El tipo numérico echo *(n)también se puede habilitar globalmente con la numericglobsortopción:

$ zsh -c 'echo *'
log log log00 log01 lóg01 Log01B log02 log0.2 log0A log0B log0C log-0D log4E log4F log50
$ zsh -o numericglobsort -c 'echo *'
log log log00 lóg01 Log01B log0.2 log0A log0B log0C log01 log02 log-0D log4E log4F log50

Si usted (como yo estaba) está confundido por ese orden en ese caso particular (aquí usando mi configuración regional británica), vea aquí para más detalles.

Stéphane Chazelas
fuente
1
El caso 'ch' puede ser aún más extraño: algunas configuraciones regionales pueden decidir que 'ch', 'Ch' y 'CH' son 1 elemento de clasificación cada una, mientras que 'cH' son dos elementos de clasificación. Ver: unicode.org/cldr/trac/ticket/889 El CLDR actual no parece ser completamente consistente: el húngaro actual ( unicode.org/cldr/trac/browser/trunk/common/collation/hu.xml ) tiene reglas como &C<cs<<<Cs<<<CS, mientras &C<cs<<<cS<<<Cs<<<CSestá marcado como un borrador experimental propuesto. A juzgar por algunos datos más antiguos importados a CLDR, los AIX y MS más antiguos parecían preferir la vista "minúsculas y luego mayúsculas son 2 elementos de clasificación diferentes".
ninjalj 01 de
Y he visto sistemas donde no funcionaba de todos modos. :(
Joshua
38

La página del manual para bash especifica:

Expansión de nombre de ruta

Después de la división de palabras, a menos que la -fopción ha sido activa, bash examina cada palabra de los personajes *, ?y [. Si aparece uno de estos caracteres, la palabra se considera un patrón y se reemplaza por una lista de nombres de archivos ordenados alfabéticamente que coinciden con el patrón [...].

usuario4556274
fuente
1
Acabo de encontrar un error interesante en la masilla o manen la representación de texto ... si el texto que estoy buscando se "ajusta", entonces un comando / search no lo encontrará. Simplemente maximicé mi terminal y ahí está :)
Wossname
2
Todo cubierto bash. Tho OP también estaba interesado en "zsh, etc."
Kusalananda
29

A menos que active algunas opciones de shell muy específicas en algunos shells, se garantiza que la salida sea la misma.

El orden se especifica en el estándar POSIX :

Si el patrón coincide con cualquier nombre de archivo o ruta existente, el patrón se reemplazará con esos nombres de archivo y ruta, ordenados de acuerdo con la secuencia de clasificación vigente en la ubicación actual . Si esta secuencia de clasificación no tiene un orden total de todos los caracteres (consulte XBD LC_COLLATE), cualquier nombre de archivo o ruta de acceso que se clasifique de igual manera se debe comparar byte a byte utilizando la secuencia de clasificación para la ubicación POSIX.

Vea también la categoría LC_COLLATE en POSIX Locale , que en resumen dice que si LC_COLLATE=C, entonces las cosas se ordenan en orden ASCII.


El bashmanual menciona

LC_COLLATE

Esta variable determina el orden de clasificación utilizado al ordenar los resultados de la expansión del nombre de ruta y determina el comportamiento de las expresiones de rango, las clases de equivalencia y las secuencias de clasificación dentro de la expansión del nombre de ruta y la coincidencia de patrones.

ksh93y zshtiene una redacción similar, lo que me lleva a creer que siguen el estándar POSIX a este respecto.

Otros shells, como pdkshy dashno dicen nada sobre la clasificación de los nombres de archivo resultantes de la globalización de nombres de archivo. Estoy tentado a creer que esto significa que todavía se adhieren al mismo estándar, al menos cuando se usa la ubicación POSIX. En mi experiencia, no he encontrado un shell que haga una clasificación abiertamente "extraña" de los nombres de archivo ASCII.

Kusalananda
fuente
2
Vea la numericglobsortopción zshque afectaría la clasificación. Aunque prefiero habilitarlo en función de cada globo, en lugar echo *(n)de activar la opción globalmente.
Stéphane Chazelas
Una trampa. Bash, en el modo predeterminado, NO es compatible con Posix.
fpmurphy 01 de
@ fpmurphy1 Di más.
Kusalananda
@Kusalananda. Bash nunca ha sido certificado como POSIX-queja. Para obtener el "cumplimiento POSIX" en Bash, debe invocar a Bash con la --posixopción de línea de comando o ejecutarset -o posix
fpmurphy
@ fpmurphy1 Sí, pero el posixmodo de Bash no afecta la ordenación de la expansión de los caracteres globales de nombre de archivo . Consulte gnu.org/software/bash/manual/html_node/Bash-POSIX-Mode.html. Esto me lleva a creer (espero, más bien) que la clasificación es compatible con POSIX.
Kusalananda
1

Si el objetivo principal es ordenar los archivos de entrada por edad, primero los más antiguos, puede escribir

(cd /tmp/logs; cat `ls -rt log*`) | grep whatever

Y si los registros rotados y comprimidos también están involucrados:

(cd /tmp/logs; zcat -f `ls -rt log*`) | grep whatever
sultansofswing
fuente
44
Se mencionó que las marcas de tiempo en los archivos no eran confiables.
Kusalananda
3
@Kusalananda, es cierto, nuestro tiempo del sistema generalmente se considera un generador de números aleatorios :)
Wossname