
Ich möchte tausende kleiner Textdateien zu einer großen Textdatei zusammenfassen. Ich habe sie in Verzeichnissen mit der Struktur: timestamp1/status.txt
. Zum Beispiel: 20130430133144/status.txt
. Bisher weiß ich, dass
cat */* > bigtextfile.txt
funktioniert bei einer kleinen Anzahl von Dateien. Aber funktioniert es auch bei einer größeren Anzahl? Ich frage mich, ob cat
der Inhalt aller Dateien gesammelt und dann versucht wird, ihn in zu speichern bigtextfile
. Andernfalls muss es wohl eine andere Möglichkeit geben, z. B. eine Datei abzurufen, sie an anzuhängen bigtextfile
, dann eine andere abzurufen und so weiter.
Antwort1
In:
cat */* > bigtextfile.txt
Die Shell erweitert */*
die sortierte Liste (nicht versteckter) übereinstimmender Dateien und wird cat
mit diesen Dateipfaden als Argumente ausgeführt.
cat
öffnet nacheinander jede Datei und schreibt in seine Standardausgabe, was es aus der Datei liest. cat
hält nicht mehr als einen Puffer voller Daten (etwa ein paar Kilobyte) gleichzeitig im Speicher.
Ein Problem, auf das Sie stoßen könnten, ist jedoch, dass die Liste der Argumente cat
so groß ist, dass sie die Obergrenze der Argumentgröße des execve()
Systemaufrufs erreicht. Daher müssen Sie die Dateiliste möglicherweise aufteilen und cat
mehrmals ausführen.
Sie könnten xargs
hierfür verwenden (hier mit GNU oder BSD xargs
für den Nicht-Standard -r
und -0
die Optionen):
printf '%s\0' */* | xargs -r0 cat -- > big-file.txt
(da printf
es in die Shell integriert ist, erfolgt es nicht über den execve
Systemaufruf und somit auch nicht über dessen Grenze).
Oder find
erstellen Sie eine Dateiliste und führen Sie so viele Cat-Befehle aus wie nötig:
find . -mindepth 2 -maxdepth 2 -type f -exec cat {} + > big-file.txt
Oder portabel:
find . -path './*/*' -prune -type f -exec cat {} + > big-file.txt
(Beachten Sie jedoch, dass im Gegensatz zu */*
versteckte Dateien (und Dateien in versteckten Verzeichnissen) eingeschlossen werden, nicht nach Dateien in symbolischen Links zu Verzeichnissen gesucht wird und die Dateiliste nicht sortiert wird).
Wenn Sie eine aktuelle Linux-Version verwenden, können Sie die Begrenzung der Argumentgröße folgendermaßen aufheben:
ulimit -s unlimited
cat -- */* > big-file.txt
Mit zsh
könnten Sie auch verwenden zargs
:
autoload zargs
zargs -- */* -- cat > big-file.txt
Mit ksh93
können Sie Folgendes verwenden command -x
:
command -x cat -- */* > big-file.txt
Alle machen dasselbe: Sie teilen die Dateiliste auf und führen so viele cat
Befehle aus wie nötig.
Mit ksh93
again können Sie die Beschränkung umgehen, execve()
indem Sie den integrierten cat
Befehl verwenden:
command /opt/ast/bin/cat -- */* > big-file.txt
Antwort2
Nein, es cat
werden nicht alle Dateien gepuffert, bevor mit dem Schreiben begonnen wird.
Wenn Sie jedoch eine große Anzahl von Dateien haben, kann es zu Problemen mit der Anzahl der an übergebenen Argumente kommen cat
. Standardmäßig erlaubt der Linux-Kernel nur die Übergabe einer festen Anzahl von Argumenten an ein Programm (ich weiß nicht mehr, wie ich den Wert bekomme, aber in den meisten Fällen sind es ein paar Tausend).
Um dieses Problem zu lösen, können Sie stattdessen Folgendes tun:
find -mindepth 2 -maxdepth 2 -type f -exec cat {} \; > bigtextfile.txt
Dies wird grundsätzlich cat
für jede einzelne gefundene Datei separat aufgerufen find
.
Antwort3
Wenn die Anzahl der Dateien zu groß ist, */*
wird eine zu große Argumentliste ausgegeben. In diesem Fall funktioniert etwas in der Art:
find . -name "*.txt" | xargs cat > outfile
(Die Idee besteht darin, find
die Dateinamen mit aufzunehmen und in einen Stream umzuwandeln. xargs
Dieser Stream wird in handliche Stücke zerlegt und an weitergegeben cat
, das sie zum Ausgabestream von verkettet xargs
und dieser geht wiederum in outfile
).