2012-05-30 2 views
5

Ich muss eine große Datendatei verarbeiten, die mit mehreren Leitungen Datensätze enthält, Beispiel Eingabe:Verwendung awk mehrzeiligen Aufzeichnung und Filterung zu identifizieren

1 Name  Dan 
1 Title  Professor 
1 Address aaa street 
1 City  xxx city 
1 State  yyy 
1 Phone  123-456-7890 
2 Name  Luke 
2 Title  Professor 
2 Address bbb street 
2 City  xxx city 
3 Name  Tom 
3 Title  Associate Professor 
3 Like  Golf 
4 Name 
4 Title  Trainer 
4 Likes  Running 

beachte, dass die erste Ganzzahl-Feld eindeutig und richtig identifiziert ein ganze Aufzeichnung. Also in der obigen Eingabe habe ich wirklich 4 Datensätze, obwohl ich nicht weiß, wie viele Zeilen von Attributen jede Datensätze haben können. Ich muss: - gültigen Datensatz identifizieren (muss "Name" und "Titel" -Feld haben) - geben Sie die verfügbaren Attribute für jeden gültigen Datensatz, sagen "Name", "Titel", "Adresse" Felder benötigt.

Beispiel Ausgabe:

1 Name  Dan 
1 Title  Professor 
1 Address aaa street 
2 Name  Luke 
2 Title  Professor 
2 Address bbb street 
3 Name  Tom 
3 Title  Associate Professor 

So in der Ausgabedatei, Rekord 4 entfernt wird, da sie die "Name" Feld doen't haben. Datensatz 3 hat kein Adressfeld, wird aber weiterhin zur Ausgabe gedruckt, da es sich um einen gültigen Datensatz mit "Name" und "Titel" handelt.

Kann ich das mit awk machen? Aber wie identifiziere ich einen ganzen Datensatz mit dem ersten "ID" -Feld in jeder Zeile?

Vielen Dank an den Unix-Shell-Script-Experten, der mir geholfen hat! :)

Antwort

6

Dies scheint zu funktionieren. Es gibt viele Möglichkeiten, dies zu tun, auch in awk.

Ich habe es zum leichteren Lesen aufgeteilt.

Beachten Sie, dass Datensatz 3 nicht angezeigt wird, da ein Feld "Adresse" fehlt, das Sie als erforderlich identifiziert haben.

#!/usr/bin/awk -f 

BEGIN { 
     # Set your required fields here... 
     required["Name"]=1; 
     required["Title"]=1; 
     required["Address"]=1; 

     # Count the required fields 
     for (i in required) enough++; 
} 

# Note that this will run on the first record, but only to initialize variables 
$1 != last1 { 
     if (hits >= enough) { 
       printf("%s",output); 
     } 
     last1=$1; output=""; hits=0; 
} 

# This appends the current line to a buffer, followed by the record separator (RS) 
{ output=output $0 RS } 

# Count the required fields; used to determine whether to print the buffer 
required[$2] { hits++ } 

END { 
     # Print the final buffer, since we only print on the next record 
     if (hits >= enough) { 
       printf("%s",output); 
     } 
} 
+0

vielen dank für ihre hilfe! Ich brauchte ein wenig Zeit, um Ihre Lösung zu verdauen und dann habe ich viel daraus gelernt !!! :) sehr geschätzt! Sparte mir so viel Zeit und lernte so viel davon :) – trillions

+0

kein Problem! Ich bin froh, dass es nützlich war. – ghoti

+0

fast +1 - meist sehr nett. Sie können die Klausel 'NR == 1' löschen. Die Frage scheint etwas unklar zu sein, ob "Adresse" erforderlich ist. Ich lese es jedoch als * not * wird benötigt (also Datensatz 3 sollte ausgegeben werden). Wenn ich das Skript mit der Kommentarzeile "Adresse" auskommentiert habe, erhalte ich alle vier Datensätze anstatt nur 1-3, obwohl "Name" für Datensatz 4 leer ist. Der Grund dafür ist, dass Ihr Selektor ' required [$ 2] 'erzeugt ein Array-Element, das jedem Feldnamen entspricht, indem es nur mit * referenziert wird und nicht überprüft, ob es gleich '1' * ist. –

3

Ich bin nicht gut in awk, aber ich würde das in Perl lösen. Hier ist eine Perl-Lösung: Für jeden Datensatz merkt er sich die wichtigen Zeilen und ob der Name und der Titel angezeigt wurden. Am Ende eines Datensatzes wird der Datensatz gedruckt, wenn alle Bedingungen erfüllt sind.

#!/usr/bin/perl 
use warnings; 
use strict; 

my ($last, $has_name, $has_title, @record); 
while (<DATA>) { 
    my ($id, $key, $value) = split; 
    if ($id != $last and @record) { 
     print @record if $has_name and $has_title; 
     undef @record; 
     undef $has_name; 
     undef $has_title; 
    } 
    $has_name = 1 if $key eq 'Name'; 
    $has_title = 1 if $key eq 'Title'; 
    push @record, $_ if grep $key eq $_, qw/Name Address Title/; 
    $last = $id; 
} 


__DATA__ 
1 Name  Dan 
1 Title  Professor 
1 Address aaa street 
1 City  xxx city 
1 State  yyy 
1 Phone  123-456-7890 
2 Name  Luke 
2 Title  Professor 
2 Address bbb street 
2 City  xxx city 
3 Name  Tom 
3 Title  Associate Professor 
3 Like  Golf 
4 Name 
4 Title  Trainer 
4 Likes  Running 
+0

vielen dank für ihre perl lösung! Ich werde es später ausprobieren. Jetzt werde ich mit awk gehen, da ich das Skript noch ein wenig für mein nächstes Bedürfnis modifizieren muss, das hier nicht gepostet wird. – trillions