grep -n | sort | sed | cut
( export LC_ALL=C
grep -n '' | sort -t: -nmk1,1 ./L - |
sed /:/d\;n | cut -sd: -f2-
) <./F
Das sollte ziemlich schnell gehen (einige zeitgesteuerte Tests sind unten aufgeführt) mit Eingaben jeder Größe. Einige Hinweise, wie:
export LC_ALL=C
- Da der Sinn der folgenden Operation darin besteht, die gesamte
./F
gestapelte Datei mit der ./L
Lineno-Datei in Einklang zu bringen, müssen wir uns nur um ASCII- [0-9]
Ziffern und den :
Doppelpunkt kümmern .
- Aus diesem Grund ist es einfacher, diese 11 Zeichen in einem Satz von 128 Möglichkeiten zu finden, als wenn UTF-8 anderweitig betroffen ist.
grep -n ''
- Dies fügt den String
LINENO:
in den Kopf jeder Zeile in stdin - or ein <./F
.
sort -t: -nmk1,1 ./L -
sort
Vernachlässigt das Sortieren seiner Eingabedateien überhaupt und geht stattdessen (korrekt) davon aus, dass sie vorsortiert sind, und -m
fügt sie in -numerically
sortierter Reihenfolge zusammen, wobei grundsätzlich alles ignoriert wird, was über ein eventuell -k1,1
vorkommendes -t:
Doppelpunkt-Zeichen hinausgeht .
- Dies kann zwar einige temporäre Speicherplätze erfordern (abhängig davon, wie weit einige Sequenzen voneinander entfernt sind) , erfordert jedoch im Vergleich zu einer geeigneten Sortierung nicht viel und ist sehr schnell, da kein Backtracking erforderlich ist.
sort
Gibt einen einzelnen Stream aus, in dem alle Linenos ./L
direkt vor den entsprechenden Zeilen stehen ./F
. ./L
Die Zeilen stehen immer an erster Stelle, weil sie kürzer sind.
sed /:/d\;n
- Wenn die aktuelle Zeile mit einem
/:/
Doppelpunkt d
übereinstimmt, wird sie aus der Ausgabe entfernt. Andernfalls werden die aktuelle und die n
ext-Zeile automatisch gedruckt .
- Und so
sed
Pflaumen sort
‚s Ausgabe nur sequenzielle Leitungspaar , die nicht einen Doppelpunkt und die folgende Zeile passen - oder, um nur eine Zeile aus ./L
und dann die nächsten.
cut -sd: -f2-
cut
-s
Uppresses von Output die seiner Eingabezeilen, die nicht mindestens einen seiner -d:
Elimiter-Strings enthalten - und so werden ./L
die Zeilen komplett beschnitten.
- Für die Zeilen, die dies tun, ist ihr erstes durch
:
Doppelpunkte getrenntes -f
Feld cut
weg - und so geht es mit allen von ihnen grep
eingefügten Linenos.
kleiner Eingangstest
seq 5 | sed -ne'2,3!w /tmp/L
s/.*/a-z &\& 0-9/p' >/tmp/F
... generiert 5 Zeilen Sample-Input. Dann...
( export LC_ALL=C; </tmp/F \
grep -n '' | sort -t: -nmk1,1 ./L - |
sed /:/d\;n | cut -sd: -f2-
)| head - /tmp[FL]
... druckt ...
==> standard input <==
a-z 1& 0-9
a-z 4& 0-9
a-z 5& 0-9
==> /tmp/F <==
a-z 1& 0-9
a-z 2& 0-9
a-z 3& 0-9
a-z 4& 0-9
a-z 5& 0-9
==> /tmp/L <==
1
4
5
größere zeitgesteuerte Tests
Ich habe ein paar ziemlich große Dateien erstellt:
seq 5000000 | tee /tmp/F |
sort -R | head -n1500000 |
sort -n >/tmp/L
... die 5mil Zeilen in /tmp/F
und 1,5mil zufällig ausgewählte Zeilen davon in setzen /tmp/L
. Ich habe dann gemacht:
time \
( export LC_ALL=C
grep -n '' | sort -t: -nmk1,1 ./L - |
sed /:/d\;n | cut -sd: -f2-
) <./F |wc - l
Es druckte:
1500000
grep -n '' \
0.82s user 0.05s system 73% cpu 1.185 total
sort -t: -nmk1,1 /tmp/L - \
0.92s user 0.11s system 86% cpu 1.185 total
sed /:/d\;n \
1.02s user 0.14s system 98% cpu 1.185 total
cut -sd: -f2- \
0.79s user 0.17s system 80% cpu 1.184 total
wc -l \
0.05s user 0.07s system 10% cpu 1.183 total
(Ich habe die Backslashes dort hinzugefügt)
Unter den derzeit hier angebotenen Lösungen ist dies die schnellste von allen, außer einer, wenn sie mit dem oben auf meinem Computer generierten Datensatz verglichen wird. Von den anderen wäre nur einer beinahe um den zweiten Platz gekämpft, und das ist meuhs perl
hier .
Dies ist keineswegs die ursprüngliche Lösung, die angeboten wird. Dank der Ratschläge und Anregungen anderer konnte die Ausführungszeit um ein Drittel verkürzt werden. Informationen zu langsameren Lösungen finden Sie im Post-Verlauf (aber warum?) .
Es ist auch erwähnenswert, dass einige andere Antworten möglicherweise besser miteinander konkurrieren, wenn nicht die Multi-CPU-Architektur meines Systems und die gleichzeitige Ausführung der einzelnen Prozesse in dieser Pipeline berücksichtigt würden. Sie arbeiten alle zur gleichen Zeit - jeder auf seinem eigenen Prozessorkern - und geben die Daten weiter und erledigen ihren kleinen Teil des Ganzen. Es ist ziemlich cool.
aber die schnellste lösung ist ...
Aber es ist nicht die schnellste Lösung. Die schnellste Lösung, die hier zweifellos angeboten wird, ist das C-Programm . Ich habe es genannt cselect
. Nachdem ich es in meine X-Zwischenablage kopiert habe, habe ich es wie folgt kompiliert:
xsel -bo | cc -xc - -o cselect
Ich habe dann gemacht:
time \
./cselect /tmp/L /tmp/F |
wc -l
... und die Ergebnisse waren ...
1500000
./cselect /tmp/L /tmp/F \
0.50s user 0.05s system 99% cpu 0.551 total
wc -l \
0.05s user 0.05s system 19% cpu 0.551 total