Tengo esta lista de archivos pdf en un directorio:
c0.pdf c12.pdf c15.pdf c18.pdf c20.pdf c4.pdf c7.pdf
c10.pdf c13.pdf c16.pdf c19.pdf c2.pdf c5.pdf c8.pdf
c11.pdf c14.pdf c17.pdf c1.pdf c3.pdf c6.pdf c9.pdf
Quiero concatenarlos usando ghostscript en orden numérico (similar a esto):
gs -q -sPAPERSIZE=a4 -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=out.pdf *.pdf
Pero el orden de expansión del shell no reproduce el orden natural de los números sino el orden alfabético:
$ for f in *.pdf; do echo $f; done
c0.pdf
c10.pdf
c11.pdf
c12.pdf
c13.pdf
c14.pdf
c15.pdf
c16.pdf
c17.pdf
c18.pdf
c19.pdf
c1.pdf
c20.pdf
c2.pdf
c3.pdf
c4.pdf
c5.pdf
c6.pdf
c7.pdf
c8.pdf
c9.pdf
¿Cómo puedo lograr el orden deseado en la expansión (si es posible sin agregar manualmente 0
-padding a los números en los nombres de archivo)?
Encontré sugerencias para usar ls | sort -V
, pero no pude hacerlo funcionar para mi caso de uso específico.
Respuestas:
Dependiendo de su entorno, puede usar
ls -v
con GNU coreutils, por ejemplo:O si tiene versiones recientes de FreeBSD u OpenBSD:
fuente
ls -v
seránatural sort of (version) numbers within text
así que también se puede usar ...-V
función desort
. Sin embargo, parece haberse extendido más, por ejemplo, tanto FreeBSD como OpenBSD losort
admiten.ls
, verifiqué si tenía una opción por sí misma en lugar de tuberías para ordenar :)Una vez más, los calificadores glob de zsh vienen al rescate.
fuente
Si todos los archivos en cuestión tienen el mismo prefijo (es decir, el texto antes del número;
c
en este caso), puede usarc?.pdf
se expande ac0.pdf
c1.pdf
...c9.pdf
.c??.pdf
se expande ac10.pdf
c11.pdf
...c20.pdf
(y hastac99.pdf
, según corresponda). Si bien cada palabra de línea de comando que contiene caracteres de expansión de nombre de ruta se expande a una lista de nombres de archivo ordenados (clasificados) de acuerdo con laLC_COLLATE
variable, las listas resultantes de la expansión de comodines adyacentes (globos) no se fusionan; simplemente están concatenados. (Me parece recordar que la página del manual de shell una vez declaró esto explícitamente, pero no puedo encontrarlo ahora).Por supuesto, si los archivos pueden subir
c999.pdf
, deberías usarlosc?.pdf c??.pdf c???.pdf
. Es cierto que esto puede volverse tedioso si tienes muchos dígitos. Puedes abreviarlo un poco; por ejemplo, para (hasta) cinco dígitos, puede usarc?{,?{,?{,?{,?}}}}.pdf
. Si su lista de nombres de archivo es escasa (p. Ej., Hay unac0.pdf
y unac12345.pdf
, pero no necesariamente todos los números intermedios), probablemente debería configurar lanullglob
opción. De lo contrario, si (por ejemplo) no tiene archivos con números de dos dígitos, obtendrá unc??.pdf
argumento literal para su programa.Si tiene varios prefijos (por ejemplo, , , y , con los números de uno o dos dígitos), se puede utilizar el método de fuerza obvia, bruta:
a<number>.pdf
b<number>.pdf
c<number>.pdf
o colapsarlo
{a,b,c}?{,?}.pdf
.fuente
ls
,stat
o cualquier otra cosa; y también funciona en bash según lo solicitado.Si no hay brechas , lo siguiente podría ser útil (aunque incompleto y no robusto con respecto a casos extremos y generalidad), solo para tener una idea:
Si puede haber lagunas, se
[ -f c${i}.pdf ]
podría agregar alguna verificación.Editar también vea esta respuesta , según la cual podría (usando Bash) usar
fuente
"$FILES"
y"$i"
) a menos que tenga una buena razón para no hacerlo y esté seguro de saber lo que está haciendo. (Por el contrario, aunque las llaves pueden ser importantes, no son tan importantes como las comillas, por lo que, por ejemplo,"c$i.pdf"
es lo suficientemente bueno). Un comando comogs [
…args…
] $FILES
, donde$FILES
contiene una lista de archivos separados por espacios, puede parecer una buena razón para use$FILES
sin citarlo (porque"$FILES"
no funcionará en ese contexto). … (Continúa)FILES=("c0.pdf")
yFILES+=("c$i.pdf")
); También esta respuesta , que utiliza la técnica que sugiero.Solo citando y arreglando la respuesta de Thor ... ¡NUNCA analices ls!
Puede usar
sort -V
(una extensión que no sea POSIX para ordenar):(para algunos comandos, aparentemente para gs es un comando así, necesita "./ " en lugar de " " ... si uno no funciona, intente con el otro)
fuente
stat
pero agregando varios otros problemas (como problemas con los nombres de archivos que comienzan con-
, problema si hay demasiados archivos,stat
siendo un comando no portátil). Y debido a que usó el operador split + glob sin ajustar IFS o deshabilitar glob, aún tendrá problemas con los nombres de archivo con espacio o tabulación o caracteres comodín.sort -V
manera confiable, necesitaría${(z)"$(printf '%s\0' * | sort -zV)"}
inzsh
(aunque yazsh
tiene(n)
para la clasificación numérica) oreadarray -td '' files < <(printf '%s\0' * | sort -zV)
inbash4.4+
.ls
sí solo (es decir, sin -l), ¿cuáles son esas otras preocupaciones ? Tenga en cuenta que--
no ayudaría para un archivo llamado-
.touch \"test\"; ls -1
por ejemplo, se muestran'"test"'
en mi ls. Simplemente no está destinado a ser analizado ... es una interfaz de usuario, no un comando de secuencias de comandos.