Concatena le linee per la prima colonna di awk o sed


12

Come posso usare awknella seguente situazione?

Voglio concatenare le linee che iniziano con la stessa colonna. Solo la prima colonna viene mantenuta dopo il join (in questo caso aaa, www, hhh).

Il file può essere separato da spazio o da tabulazione.

Esempio di input:

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

Uscita desiderata:

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

Lo sfondo è che voglio creare un database basato su file molto semplice, in cui la prima colonna è sempre l'identificatore per l'entità. Tutte le righe basate sulla stessa colonna identificatore sono concatenate.


1
da dove uuuviene la linea (nell'output)?
Saeedn,

Scusa colpa mia. Lo modificherò.
piccolo

Risposte:


8

Per ottenere le prime colonne in ogni riga usando awk puoi fare quanto segue:

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

Queste sono le tue chiavi per il resto delle linee. Quindi puoi creare una tabella hash, usando la prima colonna come chiave e la seconda colonna della linea come valore:

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

Per ottenere tutto il resto della linea, a partire dalla colonna 2, è necessario raccogliere tutte le colonne:

< 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 

Ciao, sì, aveva davvero bisogno di un'analisi dettagliata delle tabelle hash. Grazie!
piccolo

2
@tiny - Supponevo che l'ordinamento dovesse essere preservato. Non è così (questa risposta produce un ordinamento corrispondente al meccanismo di hashing, non al tuo ordine originale)?
ire_and_curses l'

3

Qualcun altro può rispondere in awk o sed, ma una versione di Python è semplice e potrebbe esserti utile.

#!/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]

Molto bello. Con la mia esperienza zero python sono anche riuscito a modificare lo script che prende il primo argomento come nome del file di input :)
piccolo

2

Questa è più di un'interessante applicazione di coreutils, sospetto che non sia molto efficiente con input di grandi dimensioni poiché invoca join per ogni riga nell'input.

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

Per migliorare la sua efficienza, il salvataggio outfilee tmpun ramdisk potrebbero aiutare.

modificare

O senza file temporanei:

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

echo "$out"

2

Ed ecco un PERL one-liner:

$ 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.