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

12

Comment puis-je l'utiliser awkdans la situation suivante?

Je veux concaténer des lignes commençant par la même colonne. Seule la première colonne est maintenue 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 désiré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

Le fond à cela est que je veux mettre en place une base de données basée sur des fichiers très simple, où 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.

minuscule
la source
1
d'où uuuvient la ligne (dans la sortie)?
saeedn
Désolé mon mauvais. Je vais le modifier.
minuscule

Réponses:

8

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

< testfile 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 deuxième colonne de la ligne comme valeur:

< testfile 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, en commençant par la colonne 2, vous devez collecter toutes les colonnes:

< 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
la source
Salut, oui, il fallait vraiment une ventilation des tables de hachage. Je vous remercie!
minuscule
2
@tiny - Je supposais que la commande devait être préservée. N'est-ce pas le cas (cette réponse produit une commande correspondant au mécanisme de hachage, pas votre commande d'origine)?
ire_and_curses
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]
ire_and_curses
la source
Très cool. Avec mon expérience zéro python, j'ai même réussi à modifier le script qui prend le premier argument comme nom de fichier d'entrée :)
minuscule
2

Il s'agit plus d'une application intéressante de coreutils, je soupçonne que ce n'est pas très efficace avec une grande entrée car elle appelle join 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é, outfileil tmppeut être utile d' enregistrer et sur un disque virtuel.

Éditer

Ou sans fichiers temporaires:

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

echo "$out"
Thor
la source
2

Et voici une doublure 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
terdon
la source