GNU/Linux >> Tutoriales Linux >  >> Linux

¿The Bash Star * Wildcard siempre produce una lista ordenada (ascendente)?

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.


Linux
  1. ¿El comando "eval" en Bash?

  2. ¿El propósito de .bashrc y cómo funciona?

  3. El Bash'?

  4. En un script Bash, ¿cómo funciona el comando Continuar con bucles incrustados?

  5. ¿Cómo personalizar Bash Autocompletar para enumerar los archivos en otro directorio?

La lista de atajos de teclado Bash útiles

Bash For Loop:la guía más práctica

¿Qué hace 'bash -c'?

¿La cuenta raíz siempre tiene UID/GID 0?

Escapar de un carácter de estrella (*) en bash

¿Qué hace la -e en un bash shebang?