Glob mit numerischer Reihenfolge

27

Ich habe diese Liste von PDF-Dateien in einem Verzeichnis:

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

Ich möchte diese mit Ghostscript in numerischer Reihenfolge verketten (ähnlich wie folgt):

gs -q -sPAPERSIZE=a4 -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=out.pdf *.pdf

Die Shell-Erweiterungsreihenfolge gibt jedoch nicht die natürliche Reihenfolge der Zahlen wieder, sondern die alphabetische Reihenfolge:

$ 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

Wie kann ich die gewünschte Reihenfolge in der Erweiterung erreichen (wenn möglich ohne manuelles Hinzufügen von 0-padding zu den Nummern in den Dateinamen)?

Ich habe Vorschläge zur Verwendung gefunden ls | sort -V, konnte sie jedoch nicht für meinen speziellen Anwendungsfall zum Laufen bringen.

huhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuh
quelle
Sie konnte nur zweistellige Zahlen in allen Fällen verwenden, so dass die alphabetische Reihenfolge der numerischen Reihenfolge übereinstimmen. Es sei denn, Sie möchten die Dinge auf die harte Tour machen.
Wildcard
1
Zumindest 3-stellige Zahlen! Erinnern Sie sich an Y2K.
Waltinator

Antworten:

12

Abhängig von Ihrer Umgebung können Sie ls -vGNU-Coreutils verwenden, z.

gs -q -sPAPERSIZE=a4 -dNOPAUSE -dBATCH -sDEVICE=pdfwrite \
   -sOutputFile=out.pdf $(ls -v)

Oder wenn Sie auf den neuesten Versionen von FreeBSD oder OpenBSD sind:

gs -q -sPAPERSIZE=a4 -dNOPAUSE -dBATCH -sDEVICE=pdfwrite \
   -sOutputFile=out.pdf $(ls | sort -V)
Thor
quelle
ls -vwird natural sort of (version) numbers within textso, dass kann auch verwendet werden ...
Sundeep
@Sundeep: In der Tat, aber dies scheint eine GNU Coreutils einzige Lösung zu sein.
Thor
Ja, scheint GNU-spezifisch zu sein - pubs.opengroup.org/onlinepubs/9699919799
Sundeep
1
@Sundeep: Die -VFunktion von sortwird auch von POSIX nicht angegeben. Es scheint sich jedoch weiter verbreitet zu haben, zum Beispiel sortunterstützen es sowohl FreeBSD als auch OpenBSD .
Thor
oh ok, kannst du diese Details hinzufügen, um auch zu antworten? Ich bin auf diese Antwort gestoßen, lsals ich nach einem ähnlichen Problem suchte (Glob in numerischer Reihenfolge) und als ich es als gebraucht ansah, habe ich geprüft, ob es eine Option für sich hatte, anstatt es zu sortieren :)
Sundeep,
12

Wenn alle fraglichen Dateien dasselbe Präfix haben (dh der Text vor der Nummer; cin diesem Fall), können Sie verwenden

gs   … args…   c? .pdf c ??. pdf

c?.pdferweitert sich zu c0.pdf c1.pdfc9.pdfc??.pdferweitert auf c10.pdf c11.pdfc20.pdf (und c99.pdfgegebenenfalls auf bis). Während jedes Befehlszeilenwort, das Pfadnamen-Erweiterungszeichen enthält, zu einer Liste von Dateinamen erweitert wird, die gemäß der LC_COLLATEVariablen sortiert (sortiert) sind, werden die Listen, die sich aus der Erweiterung benachbarter Platzhalter (Globs) ergeben, nicht zusammengeführt. Sie werden einfach verkettet. (Ich erinnere mich anscheinend, dass die Shell-Manpage dies einmal explizit angegeben hat, aber ich kann es jetzt nicht finden.)

Natürlich, wenn die Dateien gehen können c999.pdf, sollten Sie verwenden c?.pdf c??.pdf c???.pdf. Zugegeben, bei vielen Ziffern kann das langweilig werden. Sie können es ein wenig abkürzen; Sie können beispielsweise (bis zu) fünf Ziffern verwenden c?{,?{,?{,?{,?}}}}.pdf. Wenn Ihre Liste der Dateinamen dünn ist (z. B. ein c0.pdfund ein c12345.pdf, aber nicht unbedingt jede Zahl dazwischen), sollten Sie die nullglobOption wahrscheinlich festlegen . Andernfalls, wenn Sie (zum Beispiel) keine Dateien mit zweistelligen Zahlen haben, wird c??.pdfIhrem Programm ein wörtliches Argument übergeben.

Wenn Sie mehrere Präfixe (zB , und mit Zahlen von einer oder zwei Ziffern), können Sie das Offensichtliche, Brute - Force - Methode verwenden:a<number>.pdfb<number>.pdf c<number>.pdf

a?.pdf a??.pdf b?.pdf b??.pdf c?.pdf c??.pdf

oder reduzieren Sie es auf {a,b,c}?{,?}.pdf.

G-Man sagt, "Monica wiedereinsetzen"
quelle
1
Dies ist die beste Antwort , weil es jenseits aller Ansprüche von skizzen Verwendung ist ls, statoder irgendetwas anderes; und funktioniert auch in bash wie gewünscht.
Kyle
5

Wenn es keine Lücken gibt , könnte sich Folgendes als hilfreich erweisen (wenn auch lückenhaft und in Bezug auf Randfälle und Allgemeingültigkeit nicht robust) - nur um sich ein Bild zu machen:

FILES="c0.pdf"
for i in $(seq 1 20); do FILES="${FILES} c${i}.pdf"; done
gs [...args...] $FILES

Wenn es sein kann Lücken, einige [ -f c${i}.pdf ]könnten Check hinzugefügt werden.

Bearbeiten Sie auch diese Antwort , nach der Sie (mit Bash) verwenden könnten

gs [..args..] c{1..20}.pdf
sr_
quelle
Es ist im Allgemeinen eine gute Idee, die Referenzen Ihrer Shell-Variablen (z. B. "$FILES"und "$i") in Anführungszeichen zu setzen, es sei denn, Sie haben einen guten Grund, dies nicht zu tun, und Sie sind sicher, dass Sie wissen, was Sie tun. (Während geschweifte Klammern wichtig sein können, sind sie nicht so wichtig wie Anführungszeichen, daher ist sie beispielsweise "c$i.pdf"gut genug.) Ein Befehl wie , bei dem eine durch Leerzeichen getrennte Liste von Dateien enthalten ist, scheint ein guter Grund dafür zu sein Verwenden Sie, ohne es in Anführungszeichen zu setzen (da es in diesem Kontext nicht funktioniert). … (Fortsetzung)gs  [ …args… ]  $FILES$FILES$FILES"$FILES"
G-Man sagt, dass Monica
(Fortsetzung)… Unter Sicherheitsaspekte des Vergessens, eine Variable in Bash / POSIX-Shells zu zitieren , insbesondere meine Antwort darauf, finden Sie Hinweise zum Umgang mit Variablen mit mehreren Wörtern als Arrays in Bash (z. B. FILES=("c0.pdf")und FILES+=("c$i.pdf")). auch diese Antwort , die die von mir vorgeschlagene Technik verwendet.
G-Man sagt, dass Monica
1

Nur die Antwort von Thor zitieren und korrigieren ... NIEMALS ls analysieren!

Sie können sort -V(eine Nicht-POSIX-Erweiterung zum Sortieren) verwenden:

printf '%s\0' ./* | sort -zV \
    | xargs -0 gs -q -sPAPERSIZE=a4 -dNOPAUSE -dBATCH \
        -sDEVICE=pdfwrite -sOutputFile=out.pdf

(Für einige Befehle, anscheinend für gs, benötigen Sie "./ " anstelle von " " ... wenn einer nicht funktioniert, versuchen Sie den anderen)

Peter
quelle
1
Die Ausgabe von ls wird nicht analysiert, weil ls die Dateinamen mit Zeilenumbruch als Trennzeichen anzeigt, während newline genauso gültig ist wie jeder statandere Dateiname. Hier tun Sie jedoch dasselbe , fügen jedoch mehrere andere Probleme hinzu (z. B. Probleme mit Dateinamen, die beginnen) with -, Problem, wenn zu viele Dateien vorhanden sind, da states sich um einen nicht portierbaren Befehl handelt). Und da Sie den split + glob-Operator verwendet haben, ohne IFS anzupassen oder Globs zu deaktivieren, treten weiterhin Probleme mit Dateinamen mit Leerzeichen, Tabulatoren oder Platzhalterzeichen auf.
Stéphane Chazelas
Zur Nutzung GNU sort -Vzuverlässig, müssen Sie ${(z)"$(printf '%s\0' * | sort -zV)"}in zsh(obwohl zshhat (n)für die numerische Art bereits) oder readarray -td '' files < <(printf '%s\0' * | sort -zV)in bash4.4+.
Stéphane Chazelas
@Stéphanechazelas danke, und du hast recht, dass newline ein problem sein kann, aber das ist nicht der einzige grund, ls nicht zu analysieren. Und ja, ich war faul und habe auch nicht hinzugefügt. Aber ich hätte printf verwenden sollen ... das werde ich ändern.
Peter
für sich lsallein (das heißt ohne -l), was sind diese anderen Bedenken ? Beachten Sie, dass --dies für eine aufgerufene Datei nicht hilft -.
Stéphane Chazelas
@ StéphaneChazelas es gibt andere Unterschiede zwischen den Versionen ... wie einige drucken "total 0" auf dort, und die neuesten ls Versionen halten sogar Anführungszeichen um Dinge, wo Sie sie nicht wollen ... touch \"test\"; ls -1zum Beispiel zeigt '"test"'auf meinem ls. Es ist einfach nicht dazu gedacht, analysiert zu werden ... es ist eine Benutzeroberfläche, kein Skriptbefehl.
Peter