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

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

Tengo un directorio lleno de archivos con nombres como logXXdonde XX es un número hexadecimal en mayúsculas, con relleno de ceros y dos caracteres, como por ejemplo:

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

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

Deseo greprevisar todos los archivos para la entrada de registro más reciente de un determinado tipo, esperaba tener catlos 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 simplemente una implementación específica?

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

Respuesta1

En todos los shells, los globos se ordenan de forma predeterminada.Ya estaban por el /etc/globayudante.llamado por el shell de Ken Thompson para expandir los globos en la primera versión de Unix a principios de los años 70 (y que dio su nombre a los globos).

Para sh, POSIX requiere que se ordenen mediante strcoll(), es decir, utilizando el orden de clasificación en la configuración regional del usuario, aunque lsalgunos todavía lo hacen mediante strcmp(), que se basa únicamente 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 notar anteriormente que para aquellos shells que ordenan según la configuración regional, aquí en un sistema GNU con una en_GB.UTF-8configuración regional, los -nombres de los archivos se ignoran para la clasificación (la mayoría de los caracteres de puntuación lo harían). Se óordena de una manera más esperada (al menos para los británicos) y se ignora el caso (excepto cuando se trata de decidir empates).

Sin embargo, notarás 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). Se clasifican igual, por lo que obtienes resultados aleatorios.

Cambiar la ubicación afectará el orden de clasificación. Puede configurar la configuración regional en C para obtener una strcmp()clasificación similar a:

$ 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 cadenas de números exclusivamente ASCII. Como los checos (al menos en sistemas GNU), donde chhay unelemento de cotejoque se ordena después h:

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

O, como señaló @ninjalj, otros aún más extraños en lugares húngaros:

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

En zsh, puedes elegir la clasificación conclasificatorios 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) está confundido por ese orden en ese caso particular (aquí usando mi configuración regional británica), consulteaquípara detalles.

Respuesta2

La página de manual de bash especifica:

Expansión de nombre de ruta

Después de dividir las palabras, a menos que -fse haya configurado la opción, bash escanea cada palabra en busca de los caracteres *, ?y [. Si aparece uno de estos caracteres, entonces la palabra se considera como un patrón y se reemplaza con una lista ordenada alfabéticamente de nombres de archivos que coinciden con el patrón […].

Respuesta3

A menos que active algunas opciones de shell muy específicas en algunos shells, se garantiza que el resultado será el mismo.

El orden se especifica enel estándar POSIX:

Si el patrón coincide con algún nombre de archivo o ruta de acceso existente, el patrón se reemplazará con esos nombres de archivo y rutas de acceso.ordenados según 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 nombre de ruta que se clasifique por igual debe compararse byte a byte usando la secuencia de clasificación para la configuración regional POSIX.

Ver tambiénCategoría LC_COLLATE en la configuración regional POSIX, que en resumen dice que si LC_COLLATE=C, entonces las cosas están ordenadas 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 en este sentido.

Otros shells, como pdkshy, dashno dicen nada sobre la clasificación de los nombres de archivos resultantes de la agrupación de nombres de archivos. Me siento tentado a creer que esto significa que todavía cumplen con el mismo estándar, al menos cuando usan la configuración regional POSIX. En mi experiencia, no me he encontrado con un shell que ordene abiertamente "extraña" los nombres de archivos ASCII.

Respuesta4

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

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

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

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

información relacionada