Supongamos que tengo una lista de rutas de archivos almacenados en una matriz
filearray=("dir1/0010.pdf" "dir2/0003.pdf" "dir3/0040.pdf" )
Quiero ordenar los elementos en la matriz de acuerdo con los nombres básicos de los nombres de archivo, en orden numérico
sortedfilearray=("dir2/0003.pdf" "dir1/0010.pdf" "dir3/0040.pdf")
¿Cómo puedo hacer eso?
Solo puedo ordenar sus partes de nombre base:
basenames=()
for file in "${filearray[@]}"
do
filename=${file##*/}
basenames+=(${filename%.*})
done
sortedbasenamearr=($(printf '%s\n' "${basenames[@]}" | sort -n))
estoy pensando sobre
- crear una matriz asociativa cuyas claves son los nombres de base y los valores son los nombres de ruta, por lo que el acceso a los nombres de ruta siempre se realiza a través de nombres de base.
- crear otra matriz solo para nombres básicos y aplicar
sort
a la matriz de nombres básicos.
Gracias.
dir1
dir2
están inventados y en realidad son nombres de ruta arbitrarios.Respuestas:
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
orset
otypeset
(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 enbash
, de forma portátil, lo más fácil puede ser recurrir aperl
:Con
bash4.4+
, podrías hacer: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 dels
, agregue un-Mlocale
argumento aperl
. 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 decmp
(y nuevamente-Mlocale
para que se respete la marca decimal del usuario como para elsort
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:Con versiones anteriores de
bash
, podría usar unwhile IFS= read -rd ''
bucle en lugar dereadarray -d ''
operl
generar la lista de rutas correctamente citadas para que pueda pasarlaeval "array=($(perl...))"
.Con
zsh
, puede simular una expansión global para la que puede definir un orden de clasificación: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 GNUsort -V
, no losort -n
que hace una diferencia significativa al comparar1.4
y1.23
(en lugares donde.
está el signo decimal), por ejemplo), agregue eln
calificador global.En lugar de
oe{expression}
, también puede usar una función para definir un orden de clasificación como:o más avanzados como:
(entonces
a/foo2bar3.pdf
(2,3 números) se ordena despuésb/bar1foo3.pdf
(1,3) pero antesc/baz2zzz10.pdf
(2,10)) y se usa como: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:¹ Si una
strcmp()
ordenación basada en es aceptable, y para cadenas cortas, puede transformar las cadenas a su codificación hexadecimalawk
antes de pasarsort
y volver a transformar después de la ordenación.fuente
sort
en GNU coreutils permite el separador de campo personalizado y la clave. Establece/
como separador de campo y ordena en función del segundo campo para ordenar en el nombre base, en lugar de la ruta completa.printf "%s\n" "${filearray[@]}" | sort -t/ -k2
Produciráfuente
sort
, no una extensión GNU. Esto funcionará si todas las rutas tienen la misma longitud.some/long/path/0011.pdf
? Por lo que puedo ver en su página de manual,sort
sí no contiene ninguna opción para ordenar por el último campo.Ordenar con expresión gawk (compatible con bash 's
readarray
):Matriz de muestra de nombres de archivo que contienen espacios en blanco :
La salida:
Acceso a un solo artículo:
Eso supone que ninguna ruta de archivo contiene caracteres de nueva línea. Tenga en cuenta que la clasificación numérica de los valores
@val_num_asc
solo se aplica a la parte numérica principal de la clave (ninguna en este ejemplo) con respaldo a la comparación léxica (basada enstrcmp()
, no el orden de clasificación del entorno local) para los vínculos.fuente
La clasificación de los nombres de archivo con nuevas líneas en sus nombres causará problemas en el
sort
paso.Genera una
/
lista delimitadaawk
que contiene el nombre base en la primera columna y la ruta completa como las columnas restantes:Esto es lo que se ordena y
cut
se usa para eliminar la primera/
columna delimitada. El resultado se convierte en una nuevabash
matriz.fuente
/some/dir/
.a/x.c++ b/x.c-- c/x.c++
ordenaría en ese orden, aunque se-
clasifique antes+
porque-
,+
y/
el peso principal es IGNORE (por lo que compararx.c++/a/x.c++
contrax.c--/b/x.c++
primero se comparaxcaxc
conxcbxc
, y solo en caso de empate los otros pesos (donde-
viene antes+
) sería considerado./x/
lugar de/
, pero eso no resolvería el caso en el que, en la configuración regional C en sistemas basados en ASCII, sea/foo
ordenaría después,a/foo.txt
por ejemplo, porque se/
ordena después.
.Dado que "
dir1
ydir2
son nombres de ruta arbitrarios", no podemos contar con ellos consistentes en un solo directorio (o del mismo número de directorios). Por lo tanto, debemos convertir la última barra diagonal en los nombres de ruta a algo que no ocurra en otra parte del nombre de ruta. Suponiendo que el carácter@
no aparece en sus datos, puede ordenar por nombre base de esta manera:El primer
sed
comando reemplaza la última barra en cada nombre de ruta con el separador elegido, el segundo invierte el cambio. (Por simplicidad, supongo que los nombres de ruta se pueden entregar uno por línea. Si están en una variable de shell, conviértalos primero a uno por línea).fuente
cat pathnames | sed 's|\(.*\)/|\1'$'\4''|' | sort -t$'\4' -k+2nr | sed 's|'$'\4''|/|'
. (Acabo de agarrarlo\4
de la mesa ASCII. Aparentemente "¿FIN DEL TEXTO"?)\4
es^D
(control-D). A menos que lo escriba usted mismo en la terminal, es un personaje de control ordinario. En otras palabras, seguro de usar de esta manera.Solución corta (y algo rápida): al agregar el índice de matriz a los nombres de archivo y ordenarlos, más tarde podemos crear una versión ordenada basada en las indicaciones ordenadas.
Esta solución solo necesita bash builtins, así como el
sort
binario, y también funciona con todos los nombres de archivo que no incluyen un\n
carácter de nueva línea .Para cada archivo, hacemos eco de su nombre base con su índice inicial agregado de esta manera:
y luego enviado
sort -n
.Luego iteramos sobre las líneas de salida, extraemos el índice antiguo con expansión de variable bash
${line##* }
e insertamos este elemento al final de la nueva matriz.fuente
Esto ordena al anteponer los nombres de ruta del archivo con el nombre base, ordenar eso numéricamente y luego quitar el nombre base del frente de la cadena:
Sería más eficiente si tuviera los nombres de archivo en una lista que podría pasar directamente a través de una tubería en lugar de una matriz de shell, porque el trabajo real lo realiza la
sed | sort | sed
estructura, pero esto es suficiente.La primera vez que encontré esta técnica fue cuando codifiqué en Perl; en ese idioma se le conocía como Transformación Schwartziana .
En Bash, la transformación que se proporciona aquí en mi código fallará si tiene no números en el nombre base del archivo. En Perl podría codificarse de manera mucho más segura.
fuente
$@
o$*
de los argumentos de la línea de comandos para ejecutar un scriptPara nombres de archivo de igual profundidad.
Explicación
La información se toma del hombre del género.
La matriz de impresión resultante
fuente