Am einfachsten ist es wahrscheinlich, die ersten drei Felder mit awk zu kombinieren:
awk '{print $1 "_" $2 "_" $3 " " $4}' filename
Dann können Sie join
verwenden normalerweise auf "Feld 1"
Sie können dies versuchen
awk '{
o1=$1;o2=$2;o3=$3
$1=$2=$3="";gsub(" +","")
_[o1 FS o2 FS o3]=_[o1 FS o2 FS o3] FS $0
}
END{ for(i in _) print i,_[i] }' file1 file2
Ausgabe
$ ./shell.sh
foo 1 scaf 3 4.5
bar 2 scaf 3.3 1.00
foo 1 boo 2.3
Wenn Sie ungewöhnliche Zeilen weglassen möchten
awk 'FNR==NR{
s=""
for(i=4;i<=NF;i++){ s=s FS $i }
_[$1$2$3] = s
next
}
{
printf $1 FS $2 FS $3 FS
for(o=4;o<NF;o++){
printf $i" "
}
printf $NF FS _[$1$2$3]"\n"
} ' file2 file1
Ausgabe
$ ./shell.sh
foo 1 scaf 3 4.5
bar 2 scaf 3.3 1.00
Hier ist die richtige Antwort (in Bezug auf die Verwendung von Standard-GNU-Coreutils Tools und kein benutzerdefiniertes Skript in perl/awk zu schreiben Sie nennen es).
$ join -j1 -o1.2,1.3,1.4,1.5,2.5 <(<file1 awk '{print $1"-"$2"-"$3" "$0}' | sort -k1,1) <(<file2 awk '{print $1"-"$2"-"$3" "$0}' | sort -k1,1)
bar 2 scaf 3.3 1.00
foo 1 scaf 3 4.5
OK, wie funktioniert es:
-
Als erstes werden wir ein großartiges Tool
join
verwenden die zwei Zeilen zusammenführen kann.join
hat zwei Anforderungen:- Wir können mitmachen nur um ein einziges Feld.
- Beide Dateien müssen sortiert sein nach Schlüsselspalte!
-
Wir müssen Schlüssel generieren in Eingabedateien und dafür verwenden wir einen einfachen
awk
Skript:$ cat file1 foo 1 scaf 3 bar 2 scaf 3.3 $ <file1 awk '{print $1"-"$2"-"$3" "$0}' foo-1-scaf foo 1 scaf 3 bar-2-scaf bar 2 scaf 3.3
Sie sehen, wir haben die erste Spalte mit einem Schlüssel wie "foo-1-scaf hinzugefügt ".Wir machen dasselbe mit file2 .BTW.
<file awk
, ist nur eine schicke Art,awk file
zu schreiben , odercat file | awk
.Wir sollten auch sortieren unsere Dateien durch den Schlüssel, in unserem Fall ist dies Spalte 1, also fügen wir am Ende des Befehls den
| sort -k1,1
hinzu (sortieren nach Text von Spalte 1 bis Spalte 1) -
An dieser Stelle könnten wir einfach Dateien file1.with.key generieren und file2.with.key und sich ihnen anschließen, aber angenommen, diese Dateien sind riesig, wir wollen sie nicht über das Dateisystem kopieren. Stattdessen können wir etwas namens
bash
verwenden Prozesssubstitution, um Ausgaben in Named Pipes zu generieren (dies vermeidet unnötige Zwischendateierstellung). Für weitere Informationen lesen Sie bitte den bereitgestellten Link.Unsere Zielsyntax ist:
join <( some command ) <(some other command)
-
Als letztes werden ausgefallene Join-Argumente erklärt:
-j1 -o1.2,1.3,1.4,1.5,2.5
-j1
- Join durch Schlüssel in der 1. Spalte (in beiden Dateien)-
-o
- Nur diese Felder ausgeben1.2
(1. Dateifeld2),1.3
(1. Datei Spalte 3) usw.Auf diese Weise haben wir Linien verbunden, aber
join
gibt nur die notwendigen Spalten aus.
Die Lehren aus diesem Beitrag sollten sein:
- Sie sollten die coreutils beherrschen Paket sind diese Tools sehr leistungsfähig, wenn sie kombiniert werden, und Sie müssen es fast nie tun Schreiben Sie ein benutzerdefiniertes Programm, um mit solchen Fällen umzugehen,
- Kern-Utility-Tools sind außerdem blitzschnell und umfassend getestet, daher sind sie immer die beste Wahl.
Der Join-Befehl ist schwer zu verwenden und verbindet nur in einer Spalte
Ausgiebiges Experimentieren plus genaue Prüfung der Handbuchseiten zeigt, dass Sie nicht mehrere Spalten direkt verbinden können - und alle meine Arbeitsbeispiele für Joins verwenden komischerweise nur eine Joining-Spalte.
Folglich erfordert jede Lösung, dass die zu verbindenden Spalten irgendwie zu einer Spalte verkettet werden. Der standardmäßige Join-Befehl erfordert auch, dass seine Eingaben in der korrekten sortierten Reihenfolge sind - es gibt eine Bemerkung im GNU-Join (info coreutils join), dass er nicht immer sortierte Daten erfordert:
Als GNU-Erweiterung kann die Sortierreihenfolge jedoch jede Reihenfolge sein, die zwei Felder als gleich betrachtet, wenn und nur wenn der oben beschriebene Sortiervergleich sie als gleich ansieht, wenn die Eingabe keine unpaarbaren Zeilen enthält.
Ein möglicher Weg, dies mit den angegebenen Dateien zu tun, ist:
awk '{printf("%s:%s:%s %s %s %s %s\n", $1, $2, $3, $1, $2, $3, $4);}' file1 |
sort > sort1
awk '{printf("%s:%s:%s %s %s %s %s\n", $1, $2, $3, $1, $2, $3, $4);}' file2 |
sort > sort2
join -1 1 -2 1 -o 1.2,1.3,1.4,1.5,2.5 sort1 sort2
Dadurch wird am Anfang ein zusammengesetztes Sortierfeld erstellt, wobei ':' verwendet wird, um die Unterfelder zu trennen, und dann die Datei sortiert wird - für jede von zwei Dateien. Der Join-Befehl fügt dann die beiden zusammengesetzten Felder zusammen, gibt aber nur die nicht zusammengesetzten (Nicht-Join-) Felder aus.
Die Ausgabe ist:
bar 2 scaf 3.3 1.00
foo 1 scaf 3 4.5
Fehlgeschlagene Versuche, Join dazu zu bringen, das zu tun, was es nicht tun wird
join -1 1 -2 1 -1 2 -2 2 -1 3 -2 3 -o 1.1,1.2,1.3,1.4,2.4 Datei1 Datei2
Unter MacOS X 10.6.3 ergibt dies:
$ cat file1
foo 1 scaf 3
bar 2 scaf 3.3
$ cat file2
foo 1 scaf 4.5
foo 1 boo 2.3
bar 2 scaf 1.00
$ join -1 1 -2 1 -1 2 -2 2 -1 3 -2 3 -o 1.1,1.2,1.3,1.4,2.4 file1 file2
foo 1 scaf 3 4.5
bar 2 scaf 3.3 4.5
$
Dies ist (nur) das Verbinden auf Feld 3 - was nicht erwünscht ist.
Sie müssen sicherstellen, dass die Eingabedateien in der korrekten Reihenfolge sortiert sind.