Concatena linhas pela primeira coluna por awk ou sed

12

Como posso usar awk na seguinte situação?

Eu quero concatenar linhas que começam com a mesma coluna. Somente a primeira coluna é mantida após a associação (nesse caso, aaa , www , hhh ).

O arquivo pode ser separado por espaço ou por tabulações.

Exemplo de entrada:

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

Saída desejada:

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

O pano de fundo para isso é que eu quero configurar um banco de dados baseado em arquivo muito simples, em que a primeira coluna é sempre o identificador da entidade. Todas as linhas baseadas na mesma coluna identificadora são concatenadas.

    
por tiny 11.09.2012 / 08:42

4 respostas

8

Para obter as primeiras colunas em cada linha usando o awk, você pode fazer o seguinte:

cat test| awk '{print $1}'
aaa
aaa
aaa
www
hhh
hhh

Estas são suas chaves para o resto das linhas. Então você pode criar uma tabela de hash, usando a primeira coluna como uma chave e a segunda coluna da linha como o valor:

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

Para obter todo o resto da linha, começando pela coluna 2, você precisa coletar todas as colunas:

cat test| 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 

É isso aí, espero que ajude; -)

    
por 11.09.2012 / 09:26
3

Alguém pode responder no awk ou no sed, mas uma versão do Python é direta e pode ser útil para você.

#!/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]
    
por 11.09.2012 / 09:19
2

Esta é mais uma aplicação interessante de coreutils, eu suspeito que não é muito eficiente com grande entrada, uma vez que invoca a junção para cada linha na entrada.

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

Para melhorar a eficiência, salvar outfile e tmp em um disco virtual pode ajudar.

Editar

Ou sem arquivos temporários:

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

echo "$out"
    
por 11.09.2012 / 13:37
2

E aqui está um one-liner do PERL:

$ 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
    
por 11.09.2012 / 14:17