Viele Textdateien in einer großen Textdatei

Viele Textdateien in einer großen Textdatei

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 catder 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 catmit diesen Dateipfaden als Argumente ausgeführt.

catöffnet nacheinander jede Datei und schreibt in seine Standardausgabe, was es aus der Datei liest. cathä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 catso groß ist, dass sie die Obergrenze der Argumentgröße des execve()Systemaufrufs erreicht. Daher müssen Sie die Dateiliste möglicherweise aufteilen und catmehrmals ausführen.

Sie könnten xargshierfür verwenden (hier mit GNU oder BSD xargsfür den Nicht-Standard -rund -0die Optionen):

printf '%s\0' */* | xargs -r0 cat -- > big-file.txt

(da printfes in die Shell integriert ist, erfolgt es nicht über den execveSystemaufruf und somit auch nicht über dessen Grenze).

Oder finderstellen 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 zshkönnten Sie auch verwenden zargs:

autoload zargs
zargs -- */* -- cat > big-file.txt

Mit ksh93kö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 catBefehle aus wie nötig.

Mit ksh93again können Sie die Beschränkung umgehen, execve()indem Sie den integrierten catBefehl verwenden:

command /opt/ast/bin/cat -- */* > big-file.txt

Antwort2

Nein, es catwerden 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 catfü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, finddie Dateinamen mit aufzunehmen und in einen Stream umzuwandeln. xargsDieser Stream wird in handliche Stücke zerlegt und an weitergegeben cat, das sie zum Ausgabestream von verkettet xargsund dieser geht wiederum in outfile).

verwandte Informationen