Ich versuche eine naive:
$ cat * | sort -u > /tmp/bla.txt
was scheitert mit:
-bash: /bin/cat: Argument list too long
Um eine dumme Lösung wie (erstellt eine enorme temporäre Datei) zu vermeiden:
$ find . -type f -exec cat {} >> /tmp/unsorted.txt \;
$ cat /tmp/unsorted.txt | sort -u > /tmp/bla.txt
Ich dachte, ich könnte Dateien einzeln verarbeiten (dies sollte den Speicherverbrauch reduzieren und näher an einem Streaming-Mechanismus liegen):
$ cat proc.sh
#!/bin/sh
old=/tmp/old.txt
tmp=/tmp/tmp.txt
cat $old "$1" | sort -u > $tmp
mv $tmp $old
Gefolgt von:
$ touch /tmp/old.txt
$ find . -type f -exec /tmp/proc.sh {} \;
Gibt es einen einfacheren Ersatz im Unix-Stil für: cat * | sort -u
Wenn die Anzahl der Dateien erreicht ist MAX_ARG
? Es fühlt sich gut an, ein kleines Shell-Skript für eine so häufige Aufgabe zu schreiben.
sort
wird es automatisch für mehrere Dateieingang .. aber dannsort -u *
würde scheitern mitArgument list too long
als auch nehme ichAntworten:
Mit GNU
sort
und einerprintf
eingebauten Shell (alle POSIX-ähnlichen heutzutage mit Ausnahme einiger Varianten vonpdksh
):Ein Problem dabei ist, dass, da die beiden Komponenten dieser Pipeline gleichzeitig und unabhängig ausgeführt werden,
*
die rechte möglicherweise bereits dieoutput
Datei erstellt hat, die Probleme verursachen könnte (möglicherweise nicht-u
hier).output
Da es sich sowohl um eine Eingabe- als auch um eine Ausgabedatei handelt, möchten Sie möglicherweise, dass die Ausgabe in ein anderes Verzeichnis> ../output
verschoben wird ( z. B.), oder stellen Sie sicher, dass der Glob nicht mit der Ausgabedatei übereinstimmt.Eine andere Möglichkeit, dies in diesem Fall zu beheben, besteht darin, es zu schreiben:
Auf diese Weise wird es zum Schreiben
sort
geöffnetoutput
und (in meinen Tests) nicht ausgeführt, bevor die vollständige Liste der Dateien empfangen wurde (so lange, nachdem der Glob erweitert wurde). Es wird auch ein Überfallen vermieden,output
wenn keine der Eingabedateien lesbar ist.Eine andere Möglichkeit, es mit
zsh
oder zu schreibenbash
Hierbei wird die Prozessersetzung verwendet (wobei
<(...)
durch einen Dateipfad ersetzt wird, der sich auf das Leseende bezieht, in das die Pipeprintf
schreibt). Diese Funktion stammt vonksh
,ksh
besteht jedoch darauf,<(...)
ein separates Argument für den Befehl zu erweitern, damit Sie es nicht mit der--option=<(...)
Syntax verwenden können. Es würde jedoch mit dieser Syntax funktionieren:Beachten Sie, dass Sie einen Unterschied zu Ansätzen
cat
feststellen, bei denen die Ausgabe der Dateien in Fällen eingespeist wird, in denen Dateien nicht mit einem Zeilenumbruchzeichen enden:Beachten Sie außerdem, dass die
sort
Sortierung mithilfe des Kollatierungsalgorithmus in locale (strcollate()
) erfolgt undsort -u
eine von jedem Satz von Zeilen gemeldet wird, die nach diesem Algorithmus gleich sortiert sind, und keine eindeutigen Zeilen auf Byte-Ebene. Wenn Sie sich nur dafür interessieren, dass Zeilen auf Byte-Ebene eindeutig sind, und sich nicht so sehr für die Reihenfolge interessieren, in der sie sortiert sind, möchten Sie möglicherweise das Gebietsschema auf C festlegen, wobei die Sortierung auf Byte-Werten basiert (memcmp()
; das würde wahrscheinlich die Geschwindigkeit erhöhen Dinge deutlich):quelle
sort
, den Speicherverbrauch zu optimieren. Ich finde es immer nochprintf '%s\0' *
etwas komplex zu tippen.find . -type f -maxdepth 1 -print0
anstelle von verwendenprintf '%s\0' *
, aber ich kann nicht behaupten, dass es einfacher zu tippen ist. Und letzteres ist natürlich leichter als Alias zu definieren!echo
hat eine-n
, ich hätte es vorgezogen, so etwasprintf -0 %s
scheint ein wenig weniger niedrig als'%s\0'
-maxdepth
und-print0
sind GNU-Erweiterungen (obwohl heutzutage weit verbreitet). Mit anderenfind
s (wenn Sie GNU-Sortierung haben, werden Sie wahrscheinlich auch GNU finden) können Sie dies tunLC_ALL=C find . ! -name . -prune -type f ! -name '.*' -exec printf '%s\0' {} +
(LC_ALL=C
um versteckte Dateien auszuschließen, die ungültige Zeichen enthalten, selbst mit GNUfind
), aber das ist im Allgemeinen etwas übertrieben habenprintf
builtin.print0
Funktion definieren alsprint0() { [ "$#" -eq 0 ] || printf '%s\0' "$@";}
und dannprint0 * | sort...
Eine einfache Lösung funktioniert zumindest in Bash, da sie
printf
integriert ist und die Befehlszeilenargumentbeschränkungen nicht für sie gelten:(
echo * | xargs
würde auch funktionieren, außer für die Behandlung von Dateinamen mit Leerzeichen usw.)quelle
cat
für jede Datei ein separater Prozess erstellt werden muss.find -exec {} +
mehrere Dateien pro Ausführung. Damitfind -exec \;
wäre eine Katze pro Datei.Dadurch werden alle nicht ausgeblendeten regulären Dateien im aktuellen Verzeichnis verkettet und ihre kombinierten Inhalte (während doppelte Zeilen entfernt werden) in der Datei sortiert
/path/to/sorted.txt
.quelle
|
Vorgänge ordnungsgemäß verkettet werden, um die Speichernutzung zu begrenzen?sort
führt eine Sortierung außerhalb des Kerns durch, wenn die Speicheranforderungen dies erfordern. Die linke Seite der Pipeline verbraucht im Vergleich sehr wenig Speicher.Effizienz ist ein relativer Begriff, daher müssen Sie wirklich angeben, welchen Faktor Sie minimieren möchten. CPU, Speicher, Festplatte, Zeit usw. Aus Gründen der Argumentation gehe ich davon aus, dass Sie die Speichernutzung minimieren wollten und bereit sind, mehr CPU-Zyklen aufzuwenden, um dies zu erreichen. Lösungen wie die von Stéphane Chazelas funktionieren gut
Sie gehen jedoch davon aus, dass die einzelnen Textdateien zunächst einen hohen Grad an Einzigartigkeit aufweisen. Wenn nicht, dh wenn nachher
sample.srt ist mehr als 10% kleiner als sample.txt. Dann sparen Sie erheblichen Speicher, indem Sie die Duplikate in den Dateien entfernen, bevor Sie sie zusammenführen. Sie sparen außerdem noch mehr Speicher, indem Sie die Befehle nicht verketten. Dies bedeutet, dass die Ergebnisse verschiedener Prozesse nicht gleichzeitig gespeichert werden müssen.
quelle
sort
dasort
auf die Verwendung temporärer Dateien zurückgegriffen wird, wenn die Speichernutzung einen Schwellenwert überschreitet (normalerweise relativ gering).base64 /dev/urandom | sort -u
füllt Ihre Festplatte, verbraucht aber nicht viel Speicher.sort
Implementierungen der Fall , einschließlich der ursprünglichen in Unix v3 von 1972, aber anscheinend nicht beibusybox sort
. Vermutlich, weil dieser auf kleinen Systemen ohne permanenten Speicher ausgeführt werden soll.yes | sort -u
(alle duplizierten Daten) nicht mehr als ein paar Bytes Speicher benötigen, geschweige denn Festplatte. Abersort
zumindest bei GNU und Solaris werden viele 2 Byte große Dateien geschrieben/tmp
(y\n
pro paar Megabyte Eingabe), sodass die Festplatte schließlich voll wird.Wie @ilkkachu, aber die Katze (1) ist unnötig:
Wenn die Daten so lang sind, möchten Sie möglicherweise die Option sort (1) --parallel = N verwenden
Wenn N die Anzahl der CPUs ist, über die Ihr Computer verfügt
quelle