J'ai cette liste de fichiers pdf dans un répertoire:
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
Je veux les concaténer en utilisant ghostscript dans l'ordre numérique (similaire à ceci):
gs -q -sPAPERSIZE=a4 -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=out.pdf *.pdf
Mais l'ordre d'expansion du shell ne reproduit pas l'ordre naturel des nombres mais l'ordre alphabétique:
$ 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
Comment puis-je obtenir l'ordre souhaité dans l'extension (si possible sans ajouter manuellement 0
-padding aux numéros dans les noms de fichiers)?
J'ai trouvé des suggestions à utiliser ls | sort -V
, mais je n'ai pas pu le faire fonctionner pour mon cas d'utilisation spécifique.
Réponses:
Selon votre environnement, vous pouvez utiliser
ls -v
avec des coreutils GNU, par exemple:Ou si vous utilisez des versions récentes de FreeBSD ou OpenBSD:
la source
ls -v
seranatural sort of (version) numbers within text
donc utilisable aussi ...-V
fonctionnalité desort
n'est pas non plus spécifiée par POSIX. Cependant, il semble s'être propagé plus loin, par exemple, FreeBSD et OpenBSD lesort
supportent.ls
utilisé, j'ai vérifié s'il avait une option par lui-même au lieu deUne fois de plus, les qualificatifs glob de zsh viennent à la rescousse.
la source
Si tous les fichiers en question ont le même préfixe (c'est-à-dire le texte avant le numéro;
c
dans ce cas), vous pouvez utiliserc?.pdf
s'étend àc0.pdf
c1.pdf
…c9.pdf
.c??.pdf
s'étend àc10.pdf
c11.pdf
…c20.pdf
(et jusqu'àc99.pdf
, selon le cas). Bien que chaque mot de ligne de commande contenant des caractères d'extension de chemin d'accès soit développé en une liste de noms de fichiers triés (assemblés) conformément à laLC_COLLATE
variable, les listes résultant de l'expansion des caractères génériques adjacents (globs) ne sont pas fusionnées; ils sont simplement concaténés. (Il semble que je me souvienne que la page de manuel du shell l'a déjà dit explicitement, mais je ne le trouve pas maintenant.)Bien sûr, si les fichiers peuvent monter
c999.pdf
, vous devez utiliserc?.pdf c??.pdf c???.pdf
. Certes, cela peut devenir fastidieux si vous avez beaucoup de chiffres. Vous pouvez l'abréger un peu; par exemple, pour (jusqu'à) cinq chiffres, vous pouvez utiliserc?{,?{,?{,?{,?}}}}.pdf
. Si votre liste de noms de fichiers est rare (par exemple, il y a unc0.pdf
et unc12345.pdf
, mais pas nécessairement tous les nombres entre les deux), vous devriez probablement définir l'nullglob
option. Sinon, si (par exemple) vous n'avez pas de fichiers avec des nombres à deux chiffres, vous obtiendrez un littéralc??.pdf
argument passé à votre programme.Si vous avez plusieurs préfixes (par exemple , et , avec des chiffres d'un ou deux chiffres), vous pouvez utiliser l'approche de la force évidente, brute:
a<number>.pdf
b<number>.pdf
c<number>.pdf
ou le réduire
{a,b,c}?{,?}.pdf
.la source
ls
,stat
ou toute autre chose; et fonctionne également en bash comme demandé.S'il n'y a pas de lacunes , les éléments suivants pourraient s'avérer utiles (bien que sommaires et peu robustes concernant les cas marginaux et la généralité) - juste pour avoir une idée:
S'il peut y avoir des lacunes, certains
[ -f c${i}.pdf ]
vérification pourrait être ajoutée.Modifier également voir cette réponse , selon laquelle vous pourriez (en utilisant Bash) utiliser
la source
"$FILES"
et"$i"
) à moins que vous n'ayez une bonne raison de ne pas le faire et que vous êtes sûr de savoir ce que vous faites. (En revanche, si les accolades peuvent être importantes, elles ne sont pas aussi importantes que les guillemets, donc, par exemple, elles"c$i.pdf"
sont suffisantes.) Une commande comme , où contient une liste de fichiers séparés par des espaces, peut sembler une bonne raison de utiliser sans le citer (car ne fonctionnera pas dans ce contexte). … (Suite)gs [
…args…
] $FILES
$FILES
$FILES
"$FILES"
FILES=("c0.pdf")
etFILES+=("c$i.pdf")
); aussi cette réponse , qui utilise la technique que je suggère.Je ne fais que citer et corriger la réponse de Thor ... NE JAMAIS analyser ls!
Vous pouvez utiliser
sort -V
(une extension non POSIX pour trier):(pour certaines commandes, apparemment pour gs est une telle commande, vous avez besoin de "./ " au lieu de " " ... si l'une ne fonctionne pas, essayez l'autre)
la source
stat
mais en ajoutant plusieurs autres problèmes (comme des problèmes avec le démarrage des noms de fichiers avec-
, problème s'il y a trop de fichiers,stat
étant une commande non portable). Et parce que vous avez utilisé l'opérateur split + glob sans ajuster IFS ni désactiver les globs, vous aurez toujours des problèmes avec les noms de fichiers avec espace ou tabulation ou caractères génériques.sort -V
manière fiable, vous auriez besoin de${(z)"$(printf '%s\0' * | sort -zV)"}
inzsh
(bien qu'il l'zsh
ait(n)
déjà pour le tri numérique) oureadarray -td '' files < <(printf '%s\0' * | sort -zV)
inbash4.4+
.ls
seul (c'est-à-dire sans -l), quelles sont ces autres préoccupations ? Notez que--
cela n'aiderait pas pour un fichier appelé-
.touch \"test\"; ls -1
par exemple montre'"test"'
sur mon ls. Ce n'est tout simplement pas destiné à être analysé ... c'est une interface utilisateur, pas une commande de script.