Unix joint deux fichiers avec des expressions régulières en utilisant awk

J’ai un fichier (lookup.txt) qui contient une table de consultation constituée d’une liste d’expressions régulières, avec les données correspondantes (catégories et périodes). par exemple

INTERNODE|household/bills/broadband|monthly ORIGIN ENERGY|household/bills/elecsortingcity|quarterly TELSTRA.*BILL|household/bills/phone|quarterly OPTUS|household/bills/mobile|quarterly SKYPE|household/bills/skype|non-periodic 

J’ai un autre fichier (data.txt) qui contient une liste de dépenses, par exemple:

 2009-10-31,cc,-39.9,INTERNODE BROADBAND 2009-10-31,cc,-50,ORIGIN ENERGY 543546 2009-10-31,cc,-68,INTERNODE BROADBAND EXCESS CHARGES 2009-10-31,cc,-90,TELSTRA MOBILE BILL 2009-11-02,cc,-320,TELSTRA HOME BILL 2009-11-03,cc,-22.96,DICK SMITH 2009-11-03,cc,-251.24,BUNNINGS 2009-11-04,cc,-4.2,7-ELEVEN 

Je veux joindre ces deux éléments, la quasortingème colonne du fichier data.txt correspondant à l’expression régulière de la première colonne du fichier lookup.txt.

Donc, la sortie serait:

 2009-10-31,cc,-39.9,INTERNODE BROADBAND,household/bills/broadband,monthly 2009-10-31,cc,-50,ORIGIN ENERGY 543546,household/bills/elecsortingcity,quarterly 2009-10-31,cc,-68,INTERNODE BROADBAND EXCESS CHARGES,household/bills/broadband,monthly 2009-10-31,cc,-90,TELSTRA MOBILE BILL,household/bills/phone,quarterly 2009-11-02,cc,-320,TELSTRA HOME BILL,household/bills/phone,quarterly 2009-11-03,cc,-22.96,DICK SMITH 2009-11-03,cc,-251.24,BUNNINGS 2009-11-04,cc,-4.2,7-ELEVEN 

Je l’ai fait en utilisant une boucle bash, en boucle sur la recherche, en faisant des greps et en ajoutant des colonnes supplémentaires en utilisant sed, mais c’est très lent. Alors, je me demandais s’il existait une méthode plus rapide, en utilisant awk.

Toute aide serait appréciée.

 $ awk -F'|' 'FNR==NR{a[$1]=$2","$3;next}{m=split($0,b,",");for(i in a){if(b[4]~i){print $0","a[i];next}}}1' lookup file 2009-10-31,cc,-39.9,INTERNODE BROADBAND,household/bills/broadband,monthly 2009-10-31,cc,-50,ORIGIN ENERGY 543546,household/bills/elecsortingcity,quarterly 2009-10-31,cc,-68,INTERNODE BROADBAND EXCESS CHARGES,household/bills/broadband,monthly 2009-10-31,cc,-90,TELSTRA MOBILE BILL,household/bills/phone,quarterly 2009-11-02,cc,-320,TELSTRA HOME BILL,household/bills/phone,quarterly 2009-11-03,cc,-22.96,DICK SMITH 2009-11-03,cc,-251.24,BUNNINGS 2009-11-04,cc,-4.2,7-ELEVEN 

Vous pouvez le faire en Python:

 #!/usr/bin/python import csv, re lookup = [] with open('lookup.txt') as f: for rec in csv.reader(f, delimiter='|'): lookup.append((re.comstack(rec[0]), rec[1:])) with open('data.txt') as f: for rec in csv.reader(f, delimiter=','): for rexp, fields in lookup: if rexp.match(rec[3]): rec.extend(fields) break print ','.join(rec) 

Pour vos fichiers lookup.txt et data.txt il renvoie ce qui suit en moins de 0.3s:

 2009-10-31,cc,-39.9,INTERNODE BROADBAND,household/bills/broadband,monthly 2009-10-31,cc,-50,ORIGIN ENERGY 543546,household/bills/elecsortingcity,quarterly 2009-10-31,cc,-68,INTERNODE BROADBAND EXCESS CHARGES,household/bills/broadband,monthly 2009-10-31,cc,-90,TELSTRA MOBILE BILL,household/bills/phone,quarterly 2009-11-02,cc,-320,TELSTRA HOME BILL,household/bills/phone,quarterly 2009-11-03,cc,-22.96,DICK SMITH 2009-11-03,cc,-251.24,BUNNINGS 2009-11-04,cc,-4.2,7-ELEVEN 

Si vous n’avez pas les expressions rationnelles, vous pouvez utiliser la join . Combien de regexps a lookup.txt ? Si ce n’est que celui-là, développez-le et supprimez cette fonctionnalité.

Awk est vraiment conçu pour traiter un seul stream de données, un enregistrement à la fois. Ce n’est donc pas le bon outil pour ce travail. Ce serait un exercice de dix minutes en Perl ou un autre langage plus orienté vers la programmation polyvalente.

Si vous vous engagez à tout faire dans awk, écrivez un script pour générer un second script awk à partir de votre fichier de recherche qui traite les données, puis exécutez le second script.

Vous pouvez le faire en Perl. L’avantage de Perl (ou Python) est qu’ils ont des bibliothèques pour traiter les fichiers CSV. Vos exemples sont assez simples, mais que se passe-t-il si vous avez une virgule entre guillemets? Ou qu’en est-il d’utf8? etc.

La bibliothèque Perl standard pour cela est Text: CSV_XS . Cependant, c’est un peu verbeux et je préfère Parse :: CSV qui est un wrapper autour de Text :: CSV_XS.

 #!/usr/bin/perl use ssortingct; use warnings; use Parse::CSV; my %lookup; my $l = Parse::CSV->new(file => "lookup.txt", sep_char => '|'); while (my $row = $l->fetch) { my $key = qr/$row->[0]/; $lookup{$key} = [$row->[1,]]; } my $d = Parse::CSV->new(file => "data.txt"); while (my $row = $d->fetch) { foreach my $regex (keys %lookup) { if ($row->[3] =~ $regex) { push @$row, @{$lookup{$regex}}; last; } } print join(",", @$row), "\n"; }