Verketten Sie die Zeilen mit der ersten Spalte mit awk oder sed

12

Wie kann ich awkin der folgenden Situation verwenden?

Ich möchte Zeilen verketten, die mit derselben Spalte beginnen. Nur die erste Säule gehalten wird , nachdem die Verbindung (in diesem Fall aaa, www, hhh).

Die Datei kann durch Leerzeichen oder Tabulatoren getrennt sein.

Beispiel Eingabe:

aaa bbb ccc ddd NULL NULL NULL
aaa NULL NULL NULL NULL NULL NULL
aaa bbb ccc NULL NULL NULL NULL
www yyy hhh NULL NULL NULL NULL
hhh 111 333 yyy ooo hyy uuuioooy
hhh 111 333 yyy ooo hyy NULL

Gewünschte Ausgabe:

aaa bbb ccc ddd NULL NULL NULL NULL NULL NULL NULL NULL NULL bbb ccc NULL NULL NULL NULL
www yyy hhh NULL NULL NULL NULL
hhh 111 333 yyy ooo hyy uuuioooy 111 333 yyy ooo hyy NULL

Hintergrund ist, dass ich eine sehr einfache dateibasierte Datenbank einrichten möchte, bei der die erste Spalte immer die Kennung für die Entität ist. Alle Zeilen, die auf derselben Bezeichnerspalte basieren, werden verkettet.

winzig
quelle
1
Woher kam die uuuZeile (in der Ausgabe)?
Saeedn
Es tut mir leid. Ich werde es bearbeiten.
winzige

Antworten:

8

Um die ersten Spalten in jeder Zeile mit awk zu erhalten, können Sie folgende Schritte ausführen:

< testfile awk '{print $1}'
aaa
aaa
aaa
www
hhh
hhh

Dies sind Ihre Schlüssel für den Rest der Zeilen. Sie können also eine Hash-Tabelle erstellen, indem Sie die erste Spalte als Schlüssel und die zweite Spalte der Zeile als Wert verwenden:

< testfile awk '{table[$1]=table[$1] $2;} END {for (key in table) print key " => " table[key];}'
www => yyy
aaa => bbbNULLbbb
hhh => 111111

Um den gesamten Rest der Zeile, beginnend mit Spalte 2, zu erhalten, müssen Sie alle Spalten sammeln:

< testfile awk '{line="";for (i = 2; i <= NF; i++) line = line $i " "; table[$1]=table[$1] line;} END {for (key in table) print key " => " table[key];}'
www => yyy hhh NULL NULL NULL NULL 
aaa => bbb ccc ddd NULL NULL NULL NULL NULL NULL NULL NULL NULL bbb ccc    NULL NULL NULL NULL 
hhh => 111 333 yyy ooo hyy uuuioooy 111 333 yyy ooo hyy NULL 
binfalse
quelle
Hallo, ja, es musste wirklich auf Hash-Tabellen aufgeteilt werden. Vielen Dank!
winzige
2
@tiny - Ich ging davon aus, dass die Reihenfolge beibehalten werden muss. Ist dies nicht der Fall (bei dieser Antwort wird eine Bestellung erstellt, die dem Hash-Mechanismus entspricht, nicht Ihrer ursprünglichen Bestellung)?
ire_and_curses
3

Jemand anderes kann in awk oder sed antworten, aber eine Python-Version ist unkompliziert und kann für Sie hilfreich sein.

#!/usr/bin/env python

input_file = 'input.dat'
in_fh      = open(input_file, 'r')

input_order = []
seen        = {}
for line in in_fh:    
    # Remove the newline character...
    line = line[:-1]

    # Separate the first column from the rest of the line...
    key_col, sep, rest_of_line = line.partition(" ")
    rest_of_line = sep + rest_of_line  

    # If we've seen this key already, concatenate the line...
    if key_col in seen:
        seen[key_col] += rest_of_line
    # ...otherwise, record the ordering, and store the new info
    else:
        input_order.append(key_col)
        seen[key_col] = rest_of_line

in_fh.close()

# Dump the ordered output to stdout
for unique_col in input_order:
    print unique_col + seen[unique_col]
ire_and_curses
quelle
Sehr cool. Mit meiner Zero Experience Python habe ich es sogar geschafft, ein Skript zu bearbeiten, das das erste Argument als Eingabedateinamen verwendet :)
winzig,
2

Dies ist eher eine interessante Anwendung von coreutils. Ich vermute, dass sie bei großen Eingaben nicht sehr effizient ist, da sie einen Join für jede Zeile in der Eingabe aufruft.

touch outfile
while read; do
  join -a1 -a2 outfile <(echo $REPLY) > tmp
  mv tmp outfile
done < infile

Um die Effizienz zu verbessern, kann es hilfreich sein, Daten zu speichern outfileund tmpeine RAM-Disk zu erstellen.

Bearbeiten

Oder ohne temporäre Dateien:

out=""
while read; do
  out=$(join -a1 -a2 <(echo -n "$out") <(echo -n "$REPLY"))
done < infile

echo "$out"
Thor
quelle
2

Und hier ist ein PERL-Einzeiler:

$ perl -e 'my %h; while(<>){chomp; @a=split(/\s+/); $k=shift(@a); $h{$k}.=join(" ", @a) . " "; } map{$h{$_}=~s/\s*$//; print "$_ $h{$_}\n}keys(%hash);' infile
terdon
quelle