Concaténer des lignes par première colonne par awk ou sed

12

Comment puis-je utiliser awk dans la situation suivante?

Je souhaite concaténer des lignes commençant par la même colonne. Seule la première colonne est conservée après la jointure (dans ce cas, aaa , www , hhh ).

Le fichier peut être séparé par des espaces ou des tabulations.

Exemple d'entrée:

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

Sortie souhaitée:

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

L’idée de base est que je souhaite configurer une base de données très simple basée sur des fichiers, dans laquelle la première colonne est toujours l’identifiant de l’entité. Toutes les lignes basées sur la même colonne d'identifiant sont concaténées.

    
posée tiny 11.09.2012 - 08:42
la source

4 réponses

8

Pour obtenir les premières colonnes de chaque ligne à l'aide de awk, procédez comme suit:

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

Ce sont vos clés pour le reste des lignes. Vous pouvez donc créer une table de hachage en utilisant la première colonne comme clé et la seconde colonne de la ligne comme valeur:

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

Pour obtenir le reste de la ligne, à commencer par la colonne 2, vous devez collecter toutes les colonnes:

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 

C'est ça, j'espère que ça aide ;-)

    
réponse donnée 11.09.2012 - 09:26
la source
3

Quelqu'un d'autre peut répondre en awk ou sed, mais une version Python est simple et peut vous être 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]
    
réponse donnée 11.09.2012 - 09:19
la source
2

C’est plutôt une application intéressante de coreutils. Je suppose que ce n’est pas très efficace avec une entrée volumineuse, car elle appelle une jointure pour chaque ligne de l’entrée.

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

Pour améliorer son efficacité, économiser outfile et tmp sur un disque mémoire pourrait aider.

Modifier

Ou sans fichiers temporaires:

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

echo "$out"
    
réponse donnée 11.09.2012 - 13:37
la source
2

Et voici un one-liner 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
    
réponse donnée 11.09.2012 - 14:17
la source

Lire d'autres questions sur les étiquettes