Drucken einer neuen Zeile mit awk

Drucken einer neuen Zeile mit awk

Ich habe eine Menge Dateien, aus denen ich bestimmte Zeilen herausnehmen und die herausgenommenen Daten dann in eine Tabelle einfügen muss. Ein Beispiel dafür wäre meine Datei, die Folgendes zeigt:

Name: w

Age: x

Height: y

Weight: z

Ich möchte nur das Alter, die Größe und das Gewicht, also habe ich zuerst Folgendes eingegeben:

grep -E 'Age|Height|Weight' [input file] > output.txt

Aufgrund der Anzahl der Dateien sieht meine Ausgabe jetzt so aus

Age 1
 
Height 1

Weight 1

Age 2

Height 2

Weight 2

etc...

Was ich jetzt möchte, ist, ein awk-Skript auszuführen, das meine neue Datei output.txt durchgeht und zuerst alle Zeilen mit dem Wort „Alter“ findet und diese dann ausdruckt. Wenn alle Zeilen mit „Alter“ durch sind, werden die Größe und dann das Gewicht ermittelt. Ich habe das Skript ausgeführt:

awk -F"\t" '/Age/ {print} /Height/ {print}' output.txt >output2.txt

Aber es druckt es einfach wie die ursprüngliche Ausgabedatei. Wie ändere ich es, sodass es, nachdem es alle Altersangaben gemacht hat, erst die Größenangaben findet?

BEARBEITEN:

Die gewünschte Ausgabe ist die Datei

Alter 1

Alter 2

Höhe 1

Höhe 2

Gewicht 1

Gewicht 2

usw..

Nur zur Klarstellung: Alter 1 ist die Zeile mit „Alter“ aus Datei 1 usw.

Antwort1

awk durchläuft die Datei standardmäßig nur einmal und führt alle Blöcke der Reihe nach aus. Deshalb erhalten Sie die Ausgabe, die Sie erhalten haben. Sie können das gewünschte Verhalten erreichen, indem Sieeine Anordnungum die Zeilen direkt zu speichern und die Datei trotzdem nur einmal zu verarbeiten:

BEGIN {
    AgeIndex = 1
    HeightIndex = 1
}
/Age/ {
    ages[AgeIndex] = $0
    AgeIndex+=1
}
/Height/ {
    heights[HeightIndex] = $0
    HeightIndex+=1
}
END {
    for (x = 1; x < AgeIndex; x++)
        print ages[x] "\n"
    for (x = 1; x < HeightIndex; x++)
        print heights[x] "\n"
}

Speichern Sie das beispielsweise in filter.awkund führen Sie dann Folgendes aus:

awk -f filter.awk output.txt > output2.txt

um die gewünschte Ausgabe zu erhalten:

$ awk -f filter.awk < data
Age 1

Age 2

Height 1

Height 2

Wir erstellen zwei Arrays agesund heightsspeichern dabei jede passende Zeile darin. AgeIndexgibt an, wie weit wir im Array gekommen sind. Am Ende drucken wir jede Zeile aus, die wir gespeichert haben (und eine zusätzliche neue Zeile, wie Sie möchten), zuerst alle Altersangaben, dann alle Körpergrößen.

Die Arrays halten am Ende die gesamte Datei im Speicher. Wenn Ihre Datei also besonders groß ist, müssen Sie diesen Speicherverbrauch gegen die Zeit eintauschen, die Sie für das mehrmalige Durchgehen der gesamten Datei benötigen. An diesem Punkt ist es im Wesentlichen dasselbe wie bei einem Programm in jeder anderen Sprache. Wenn Sie keinen besonderen Grund haben, awk zu verwenden, ziehen Sie möglicherweise eine andere Sprache vor. Ehrlich gesagt würde ich das empfehlen – awk bringt Ihnen hier nicht viel.

Antwort2

Mit gawk:

$ awk -F"\t" '
    { a[$1]++ }
    END {
        n = asorti(a,b);
        for (i = 1; i <= n; i++) {
            print b[i];
            if (i%2 == 0) {
                printf "\n";
            }
        }
    }
' output.txt
Age 1
Age 2

Height 1
Height 2

Weight 1
Weight 2

Antwort3

Ich gehe davon aus, dass die Leerzeilen nicht Teil Ihrer eigentlichen Datei sind oder dass sie Ihnen zumindest egal sind. Wenn das der Fall ist, brauchen Sie nur sort:

$ cat output.txt
Age 1
Height 1
Weight 1
Age 2
Height 2
Weight 2

$ sort output.txt
Age 1
Age 2
Height 1
Height 2
Weight 1
Weight 2

Sofern Ihre Dateien jedoch nicht zu groß für den Speicher sind, ist es möglicherweise einfacher, die ganze Sache in einem einzigen Schritt zu erledigen:

grep -whE 'Age|Height|Weight' *txt | sort > outfile

Das Obige sucht nach Ageoder Heightoder in allen Dateien, deren Name im aktuellen Verzeichnis Weightendet ( ). Das bedeutet „nur ganze Wörter abgleichen“ (das trifft also beispielsweise nicht zu), das ist notwendig, weil ohne es der Name der Datei zusammen mit der übereinstimmenden Zeile gedruckt wird, wenn mehr als eine Eingabedatei angegeben ist. Das ermöglicht erweiterte reguläre Ausdrücke, die uns für ODER geben.txt*txt-wAgeAgeing-h-E|

NOTIZ: Wenn Sie aus irgendeinem Grund tatsächlich die zusätzliche Leerzeile zwischen den einzelnen Einträgen wünschen (was Ihr grepBefehl jedoch nicht bewirken würde), können Sie sie folgendermaßen hinzufügen:

grep -whE 'Age|Height|Weight' *txt | sort | sed 's/$/\n/'

Beispiel

$ for i in {1..3}; do echo -e "Name $i\nAge $i\nHeight $i\nWeight $i" > $i.txt; done
$ for f in *txt; do echo " -- $f --"; cat $f; done
 -- 1.txt --
Name 1
Age 1
Height 1
Weight 1
 -- 2.txt --
Name 2
Age 2
Height 2
Weight 2
 -- 3.txt --
Name 3
Age 3
Height 3
Weight 3

$ grep -whE 'Age|Height|Weight' *txt | sort
Age 1
Age 2
Age 3
Height 1
Height 2
Height 3
Weight 1
Weight 2
Weight 3

Selbst wenn sortes für Sie nicht das Richtige ist, würde ich Folgendes in Perl tun awk(vorausgesetzt, Sie möchten die zusätzlichen Leerzeilen, was wahrscheinlich wiederum nicht der Fall ist):

$ perl -ane '$k{$F[0]}.=$_."\n" if /./; 
    END{print $k{$_},"\n" for sort keys (%k)}' output.txt 
Age 1

Age 2


Height 1

Height 2


Weight 1

Weight 2


 

Sie können dies weitergeben, head -n -2um die letzten beiden Leerzeilen zu entfernen, wenn Sie sie nicht möchten.

Antwort4

pythonLösung für dieses Problem:

from collections import defaultdict
f = open("output.txt", "r")
d = defaultdict(list)
for line in f:
   line = line.strip()
   if line != '':
     arr = line.split(" ")
     d[arr[0]].append(arr[1])
print d.items()

Ich habe die erste Spalte gehasht und in eine Liste eingefügt.

verwandte Informationen