Ich habe über 15.000 Dateien , die genannt werden file_1.pdb
, file_2.pdb
usw. Ich kann Katze über ein paar tausend von ihnen um , indem Sie:
cat file_{1..2000}.pdb >> file_all.pdb
Wenn ich dies jedoch für 15.000 Dateien tue, wird der Fehler angezeigt
-bash: /bin/cat: Argument list too long
Ich habe gesehen, dass dieses Problem dadurch gelöst wird, find . -name xx -exec xx
aber dadurch wird die Reihenfolge, in der die Dateien verbunden werden, nicht beibehalten. Wie kann ich das erreichen?
files
find
cat
brace-expansion
Natriumnitrat
quelle
quelle
cat file_{1..15000}.pdb
Konstrukt funktioniert einwandfrei für mich.getconf ARG_MAX
sollte erzählen.Antworten:
Mit
find
,sort
undxargs
:Der
find
Befehl findet alle relevanten Dateien und gibt dann ihre Pfadnamen aus,sort
um sie in der richtigen Reihenfolge zu sortieren (wenn die Zahlen in den Dateinamen auf eine feste Breite mit Nullen aufgefüllt worden wären, hätten wir sie nicht benötigt-V
).xargs
Nimmt diese Liste sortierter Pfadnamen und führtcat
diese in möglichst großen Stapeln aus.Dies sollte auch dann funktionieren, wenn die Dateinamen seltsame Zeichen wie Zeilenumbrüche und Leerzeichen enthalten. Wir verwenden
-print0
withfind
, umsort
Namen zu sortieren, die mit Nullen abgeschlossen sind, undsort
behandeln diese mithilfe von-z
.xargs
Auch liest Nul-terminierte Namen mit seinem-0
Flag.Beachten Sie, dass ich das Ergebnis in eine Datei schreibe, deren Name nicht mit dem Muster übereinstimmt
file_*.pdb
.Die obige Lösung verwendet einige nicht standardmäßige Flags für einige Dienstprogramme. Diese werden von der GNU-Implementierung dieser Dienstprogramme und zumindest von der OpenBSD- und der macOS-Implementierung unterstützt.
Die verwendeten Nicht-Standard-Flags sind
-maxdepth 1
, umfind
nur das oberste Verzeichnis, aber keine Unterverzeichnisse einzugeben. POSIXly verwendenfind . ! -name . -prune ...
-print0
, umfind
Pfadnamen mit nicht abgeschlossener Ausgabe zu erstellen (dies wurde von POSIX berücksichtigt, aber abgelehnt). Man könnte-exec printf '%s\0' {} +
stattdessen verwenden.-z
, umsort
nicht terminierte Datensätze aufzunehmen. Es gibt keine POSIX-Äquivalenz.-V
,sort
sortieren zB200
nach3
. Es gibt keine POSIX-Entsprechung, diese kann jedoch durch eine numerische Sortierung bestimmter Teile des Dateinamens ersetzt werden, wenn die Dateinamen ein festes Präfix haben.-0
, um nichtxargs
abgeschlossene Datensätze zu lesen. Es gibt keine POSIX-Äquivalenz. POSIXly müsste man die Dateinamen in einem Format angeben, das von erkannt wirdxargs
.Wenn sich die Pfadnamen gut verhalten und die Verzeichnisstruktur flach ist (keine Unterverzeichnisse), könnte man
-V
mit Ausnahme von mit auf diese Flags verzichtensort
.quelle
printf ‘file_%d.pdb\0’ {1..15000} | xargs -0 cat
oder sogar mit Kevins Punkt schreibenecho file_{1..15000}.pdb | xargs cat
. Diefind
Lösung hat erheblich mehr Aufwand, da das Dateisystem nach diesen Dateien durchsucht werden muss, ist jedoch nützlicher, wenn einige der Dateien möglicherweise nicht vorhanden sind.xargs
eher alscat
dass umgeleitet wird (jedercat
Aufruf wird verwendenxargs
Standardausgabe). Wenn wir das gesagtxargs -0 sh -c 'cat >all.pdb'
hätten, hätte es Sinn gemacht,>>
statt zu verwenden>
, wenn Sie das angedeutet haben.sort -n -k1.6
würde es funktionieren (für das Original,file_nnn
Dateinamen odersort -n -k1.5
für diejenigen ohne Unterstrich).Mit
zsh
(woher dieser{1..15000}
Operator kommt):Oder für alle
file_<digits>.pdb
Dateien in numerischer Reihenfolge:(Wo
<x-y>
ist ein Glob-Operator, der mit den Dezimalzahlen x bis y übereinstimmt? Ohnex
Nory
ist dies eine beliebige Dezimalzahl. Entsprichtextendedglob
's[0-9]##
oderkshglob
' s+([0-9])
(einer oder mehreren Ziffern)).Mit
ksh93
, unter Verwendung seines eingebautencat
Befehls (also nicht betroffen von dieser Begrenzung desexecve()
Systemaufrufs, da es keine Ausführung gibt ):Mit
bash
/zsh
/ksh93
(die Unterstützungzsh
‚s{x..y}
und habenprintf
builtin):Auf einem GNU-System oder einem kompatiblen System können Sie auch Folgendes verwenden
seq
:Bei den
xargs
Lösungen auf der Grundlage von Leerzeichen, einfachen oder doppelten Anführungszeichen oder umgekehrten Schrägstrichen müsste besondere Sorgfalt auf Dateinamen verwendet werden.Wie für
-It's a trickier filename - 12.pdb
, verwenden Sie:quelle
seq -f | xarg cat >
ist die eleganteste und effektivste Lösung. (MEINER BESCHEIDENEN MEINUNG NACH).'"./-It'\''s a trickier filename - %.17g.pdb"'
?Eine for-Schleife ist möglich und sehr einfach.
Der Nachteil ist, dass Sie
cat
sehr oft anrufen . Aber wenn Sie sich nicht genau erinnern können, wie Sie die Dinge anstellen sollenfind
und der Aufwand für das Aufrufen in Ihrer Situation nicht allzu schlimm ist, sollten Sie dies berücksichtigen.quelle
echo $i;
in der Schleife Körper als "Fortschrittsanzeige"quelle
seq -f file_%.10g.pdb 15000
. Beachten Sie, dass diesseq
kein Standardbefehl ist.seq -f
ist eine großartige Möglichkeit. werde mich daran erinnern.Prämisse
Dieser Fehler sollte nicht bei nur 15.000 Dateien mit diesem bestimmten Namensformat auftreten [ 1 , 2 ] .
Wenn Sie diese Erweiterung in einem anderen Verzeichnis ausführen und den Pfad zu jeder Datei hinzufügen müssen, ist der Umfang Ihres Befehls größer und kann natürlich auftreten.
Lösung führen Sie den Befehl aus diesem Verzeichnis.
Beste Lösung Wenn ich stattdessen schlecht geraten habe und Sie es aus dem Verzeichnis ausführen, in dem sich die Dateien befinden ...
IMHO ist die beste Lösung die von Stéphane Chazelas :
mit printf oder seq; Getestet mit 15k-Dateien, bei denen nur deren Nummer im Cache gespeichert ist, ist dies sogar die schnellere (derzeit und mit Ausnahme der OP-Datei aus demselben Verzeichnis, in dem sich die Dateien befinden).
Noch ein paar Worte
Sie sollten in der Lage sein, Ihre Shell-Befehlszeilen länger zu übergeben.
Ihre Befehlszeile ist 213914 Zeichen lang und enthält 15003 Wörter
cat file_{1..15000}.pdb " > file_all.pdb" | wc
... sogar das Hinzufügen von 8 Bytes für jedes Wort liegt 333.938 Bytes (0,3 MB) weit unter dem Wert von 2097142 (2,1 MB), der von
ARG_MAX
einem Kernel 3.13.0 oder dem geringfügig kleineren Wert von 2088232 gemeldet wurde, der als "Maximale Befehlslänge, die wir tatsächlich erreichen könnten" angegeben wurde use " vonxargs --show-limits
Sehen Sie sich die Ausgabe von auf Ihrem System an
Laziness geführte Lösung
In solchen Fällen arbeite ich am liebsten mit Blöcken, auch weil sich in der Regel eine zeitsparende Lösung ergibt.
Die Logik (falls vorhanden) ist, dass ich viel zu faul bin, um 1 ... 1000 1001..2000 usw. usw. zu schreiben.
Deshalb bitte ich ein Skript, dies für mich zu tun.
Erst nachdem ich die Ausgabe auf Korrektheit überprüft habe, leite ich sie in ein Skript um.
... aber Faulheit ist ein Geisteszustand .
Da ich allergisch gegen
xargs
(ich hätte esxargs
hier wirklich verwenden sollen ) bin und nicht prüfen wollen, wie ich es benutze, beende ich die Neuerfindung des Rads pünktlich wie in den folgenden Beispielen (tl; dr).Da die Dateinamen kontrolliert werden (keine Leerzeichen, Zeilenumbrüche ...), können Sie problemlos mit dem folgenden Skript arbeiten.
tl; dr
Version 1: Übergeben Sie als optionalen Parameter die 1. Dateinummer, die letzte, die Blockgröße und die Ausgabedatei
Version 2
Aufruf von bash für die Erweiterung (in meinen Tests etwas langsamer ~ 20%).
Natürlich können Sie
seq
[ 3 ] (von coreutils) loswerden und direkt mit den Variablen in bash arbeiten oder Python verwenden oder ein Programm kompilieren, um dies zu tun [ 4 ] ...quelle
%g
die Abkürzung für ist%.6g
. Es würde zum Beispiel 1.000.000 als 1e + 06 darstellen.xargs
, zsh deszargs
oderksh93
‚scommand -x
.seq
ist kein bash builtin, sondern ein Befehl von GNU coreutils.seq -f %g 1000000 1000000
gibt 1e + 06 auch in der neuesten Version von coreutils aus.xarg
... aber ich verstehe, dass sie persönlich ist und vielleicht nur mit mir zusammenhängt.Ein anderer Weg, dies zu tun, könnte sein
quelle