Al contrario de ksh o zsh, bash no tiene soporte incorporado para ordenar matrices o listas de cadenas arbitrarias. Puede ordenar los globos o la salida de alias
or set
o typeset
(aunque los últimos 3 no están en el orden de clasificación local del usuario), pero eso no se puede usar prácticamente aquí.
No hay nada en el cofre de herramientas POSIX que pueda ordenar fácilmente listas arbitrarias de cadenas tampoco ( sort
ordena líneas, por lo que solo las secuencias cortas de caracteres (LINE_MAX a menudo son más cortas que PATH_MAX) de caracteres que no sean NUL y nueva línea, mientras que las rutas de archivo son secuencias no vacías de bytes. que 0).
Entonces, si bien podría implementar su propio algoritmo de clasificación en awk
(usando el <
operador de comparación de cadenas) o inclusobash
(usando [[ < ]]
), para rutas arbitrarias en bash
, de forma portátil, lo más fácil puede ser recurrir a perl
:
Con bash4.4+
, podrías hacer:
readarray -td '' sorted_filearray < <(perl -MFile::Basename -l0 -e '
print for sort {basename($a) cmp basename($b)} @ARGV' -- "${filearray[@]}")
Eso da una strcmp()
orden similar. Para un orden basado en las reglas de intercalación de la configuración regional como en globos o la salida de ls
, agregue un -Mlocale
argumento a perl
. Para la ordenación numérica (más como GNU, sort -g
ya que admite números como +3
, 1.2e-5
y no miles de separadores, aunque no hexadimales), use en <=>
lugar de cmp
(y nuevamente -Mlocale
para que se respete la marca decimal del usuario como para el sort
comando).
Estará limitado por el tamaño máximo de argumentos para un comando. Para evitar eso, puede pasar la lista de archivos a perl
su stdin en lugar de a través de argumentos:
readarray -td '' sorted_filearray < <(
printf '%s\0' "${filearray[@]}" | perl -MFile::Basename -0le '
chomp(@files = <STDIN>);
print for sort {basename($a) cmp basename($b)} @files')
Con versiones anteriores de bash
, podría usar un while IFS= read -rd ''
bucle en lugar de readarray -d ''
o perl
generar la lista de rutas correctamente citadas para que pueda pasarla eval "array=($(perl...))"
.
Con zsh
, puede simular una expansión global para la que puede definir un orden de clasificación:
sorted_filearray=(/(e{'reply=($filearray)'}oe{'REPLY=$REPLY:t'}))
Con reply=($filearray)
realmente forzamos la expansión global (que inicialmente era justa /
) para ser los elementos de la matriz. Luego definimos el orden de clasificación para que se base en la cola del nombre de archivo.
Para un strcmp()
orden similar, fije la configuración regional en C. Para la ordenación numérica (similar a GNU sort -V
, no lo sort -n
que hace una diferencia significativa al comparar 1.4
y 1.23
(en lugares donde .
está el signo decimal), por ejemplo), agregue el n
calificador global.
En lugar de oe{expression}
, también puede usar una función para definir un orden de clasificación como:
by_tail() REPLY=$REPLY:t
o más avanzados como:
by_numbers_in_tail() REPLY=${(j:,:)${(s:,:)${REPLY:t}//[^0-9]/,}}
(entonces a/foo2bar3.pdf
(2,3 números) se ordena después b/bar1foo3.pdf
(1,3) pero antes c/baz2zzz10.pdf
(2,10)) y se usa como:
sorted_filearray=(/(e{'reply=($filearray)'}no+by_numbers_in_tail))
Por supuesto, esos se pueden aplicar en globos reales, ya que para eso están destinados principalmente. Por ejemplo, para una lista de pdf
archivos en cualquier directorio, ordenados por basename / tail:
pdfs=(**/*.pdf(N.oe+by_tail))
¹ Si una strcmp()
ordenación basada en es aceptable, y para cadenas cortas, puede transformar las cadenas a su codificación hexadecimal awk
antes de pasar sort
y volver a transformar después de la ordenación.