Ich verwende viel grep awk-Sortierung in meiner Unix-Shell, um mit mittelgroßen (ca. 10 bis 100 Millionen Zeilen) tabulatorgetrennten Spaltentextdateien zu arbeiten. In dieser Hinsicht ist die Unix-Shell meine Tabelle.
Aber ich habe ein großes Problem, nämlich die Auswahl von Datensätzen mit einer Liste von IDs.
Wenn Sie eine table.csv
Datei mit Format id\tfoo\tbar...
und eine ids.csv
Datei mit einer Liste von IDs haben, wählen Sie nur Datensätze aus, in table.csv
denen eine ID vorhanden ist ids.csv
.
Art von /programming/13732295/extract-all-lines-from-text-file-based-on-a-given-list-of-ids, aber mit Shell, nicht Perl.
grep -F
erzeugt offensichtlich falsch positive Ergebnisse, wenn die IDs eine variable Breite haben.
join
ist ein Dienstprogramm, das ich nie herausfinden konnte. Zuallererst erfordert es eine alphabetische Sortierung (meine Dateien sind normalerweise numerisch sortiert), aber selbst dann kann ich es nicht zum Laufen bringen, ohne mich über falsche Reihenfolge zu beschweren und einige Datensätze zu überspringen. Also ich mag es nicht. grep -f gegen Datei mit ^id\t
-s ist sehr langsam, wenn die Anzahl der IDs groß ist.
awk
ist umständlich.
Gibt es dafür gute Lösungen? Gibt es spezielle Tools für durch Tabulatoren getrennte Dateien? Zusätzliche Funktionen sind ebenfalls sehr willkommen.
UPD: Korrigiert sort
->join
grep -f
es zu langsam ist, klingt die Beibehaltung dieser Strategie nach mehr Ärger als es wert ist - Variationen werden wahrscheinlich denselben O (N * M) -Leistungsproblemen zum Opfer fallen. Vielleicht ist es besser, wenn Sie lernen, wie man eine normalisierte SQL-awk
.sort
kann alle Arten von Sortierungen, numerischen, alphabetischen und anderen durchführen. Sieheman sort
.Antworten:
Ich denke du meintest es
grep -f
nicht,grep -F
aber du brauchst tatsächlich eine Kombination aus beidem und-w
:Der Grund, warum Sie falsch positive Ergebnisse erhalten haben, ist (ich denke, Sie haben es nicht erklärt), denn wenn eine ID in einer anderen enthalten sein kann, werden beide gedruckt.
-w
Behebt dieses Problem und-F
stellt sicher, dass Ihre Muster als Zeichenfolgen und nicht als reguläre Ausdrücke behandelt werden. Vonman grep
:Wenn Ihre Fehlalarme darauf zurückzuführen sind, dass eine ID in einem Nicht-ID-Feld vorhanden sein kann, durchlaufen Sie stattdessen Ihre Datei:
oder schneller:
Persönlich würde ich dies jedoch tun
perl
:quelle
^
mit -F verwenden können, können Sie die erste Spalte nicht speziell ausrichten.^id\t
vom OP implizierte Bit kannid
in einer anderen Spalte auftreten. Wenn nicht, spielt das keine Rolle.Das
join
Dienstprogramm ist das, was Sie wollen. Die Eingabedateien müssen lexikalisch sortiert sein.Angenommen, Ihre Shell ist bash oder ksh:
Ohne sortieren zu müssen, ist die übliche awk-Lösung
quelle
join
ist kein Kludge: Ihre Worte waren, als Sie es nicht herausfinden konnten. Öffne deinen Geist und lerne. Welche Ausgabe haben Sie erhalten und wie unterscheidet sich das von Ihren Erwartungen?join
.awk
Lösung hier ist für meine Zwecke sehr schnell und effizient (ich extrahiere Teilmengen von einigen hundert aus Dateien mit ~ 100 Millionen Zeilen)Die Antworten auf diese SO-Frage haben mir geholfen, die Probleme mit Join zu umgehen. Wenn Sie die Datei sortieren, um sie für den Beitritt zu senden, müssen Sie im Wesentlichen sicherstellen, dass Sie nach der Spalte sortieren, zu der Sie beitreten. Wenn dies also das erste ist, müssen Sie ihm mitteilen, was das Trennzeichen in der Datei ist und dass es nach dem ersten Feld (und nur nach dem ersten Feld) sortiert werden soll. Andernfalls können Ihre Trennzeichen und möglicherweise andere Felder die Sortierreihenfolge beeinflussen, wenn das erste Feld beispielsweise eine variable Breite hat.
Verwenden Sie also die Sortieroption -t, um Ihr Trennzeichen anzugeben, und die Option -k, um das Feld anzugeben (denken Sie daran, dass Sie ein Start- und ein Endfeld benötigen - auch wenn es dasselbe ist - oder es wird nach diesem Zeichen sortiert bis zum Ende der Zeile).
Für eine durch Tabulatoren getrennte Datei wie in dieser Frage sollte Folgendes funktionieren (dank der Antwort von glenn für die Struktur):
join -t$'\t' <(sort -d ids.csv) <(sort -d -t$'\t' -k1,1 table.csv) > output.csv
(Als Referenz bedeutet das Flag -d die Wörterbuchsortierung. Möglicherweise möchten Sie auch das Flag -b verwenden, um führende Leerzeichen zu ignorieren (siehe
man sort
undman join
).Angenommen, Sie verbinden zwei durch Kommas getrennte Dateien -
input1.csv
in der dritten undinput2.csv
in der vierten Spalte . Du könntest benutzenjoin -t, -1 3 -2 4 <(sort -d -t, -k3,3 input2.csv) <(sort -d -t, -k4,4 input2.csv) > output.csv
Hier geben die Optionen
-1
und an-2
, welche Felder in der ersten bzw. zweiten Eingabedatei verknüpft werden sollen.quelle
Sie können Ruby auch verwenden, um etwas Ähnliches zu tun:
quelle