Tengo un directorio lleno de archivos con nombres como logXX
donde XX es un número hexadecimal en mayúsculas de dos caracteres, rellenado con ceros, como:
log00
log01
log02
...
log0A
log0B
log0C
...
log4E
log4F
log50
...
Generalmente habrá menos de 20 o 30 archivos en total. La fecha y la hora de mi sistema en particular no son algo en lo que se pueda confiar (un sistema integrado sin fuentes de tiempo NTP o GPS confiables). Sin embargo, los nombres de los archivos aumentarán de manera confiable como se muestra arriba.
Deseo grep
a través de todos los archivos para la entrada de registro más reciente de un determinado tipo, esperaba cat
los archivos juntos como...
cat /tmp/logs/log* | grep 'WARNING 07 -' | tail -n1
Sin embargo, se me ocurrió que diferentes versiones de bash
o sh
o zsh
etc. pueden tener diferentes ideas sobre cómo el *
se expande.
El man bash
la página no dice si la expansión de *
sería definitivamente una lista alfabética ascendente de nombres de archivo coincidentes. Parece estar ascendiendo cada vez que lo probé 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 en orden alfabético?
Respuesta aceptada:
En todos los shells, los globos se ordenan de forma predeterminada. Ya estaban por el /etc/glob
ayudante llamado por el caparazón de Ken Thompson para expandir globs en la primera versión de Unix a principios de los 70 (y que dio a globs su nombre).
Para sh
, POSIX requiere que se ordenen mediante strcoll()
, que utiliza el orden de clasificación en la configuración regional del usuario, como para ls
aunque 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 notar arriba que para aquellos shells que clasifican según la configuración regional, aquí en un sistema GNU con un en_GB.UTF-8
configuración regional, el -
en los nombres de archivo se ignora para ordenar (la mayoría de los caracteres de puntuación lo harían). El ó
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á 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 ordenan de la misma manera, por lo que obtienes resultados aleatorios.
Relacionado:¿Procesar descendientes?
Cambiar la configuración regional afectará el orden de clasificación. Puede establecer la configuración regional en C para obtener un strcmp()
-como ordenar:
$ 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-aSCII all-alnum. Como los checos (al menos en sistemas GNU) donde ch
es un elemento de clasificación que ordena después de h
:
$ LC_ALL=cs_CZ.UTF-8 bash -c 'echo *'
log0Ah log0Bh log0Dh log0Ch
O, como lo señaló @ninjalj, incluso 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
, 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 de echo *(n)
también se puede habilitar globalmente con numericglobsort
opció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), consulte aquí para obtener más detalles.