Wie teile ich eine Datei nach einer Spalte (einschließlich Header) und benenne die generierten Dateien um?

7

Ich habe eine .txt, die so veranschaulicht werden kann:

NAME | CODE
name1 | 001
name2 | 001
name3 | 002
name4 | 003
name5 | 003
name6 | 003

Ich muss ein Skript schreiben, um diese Datei entsprechend der CODESpalte aufzuteilen. In diesem Fall würde ich Folgendes erhalten:

file 1:
NAME | CODE
name1 | 001
name2 | 001

file 2:
NAME | CODE
name3 | 002

file 3:
NAME | CODE
name4 | 003
name5 | 003
name6 | 003

Nach einigen Untersuchungen würde die Verwendung von awk funktionieren:

$ awk -F, '{print > $2".txt"}' inputfile

Die Sache ist, ich muss auch den Header in die erste Zeile einfügen und ich muss die Dateinamen unterschiedlich sein. Anstelle von 001.txtzum Beispiel muss der Dateiname so etwas wie sein FILE_$FILENAME_IDK.txt.

Geralt
quelle

Antworten:

8

Sie könnten es so versuchen:

awk 'NR==1{h=$0; next}
!seen[$3]++{f="FILE_"FILENAME"_"$3".txt";print h > f} 
{print >> f}' infile

Das Obige speichert den Header in einer Variablen h( NR==1{h=$0; next}). Wenn er $3nicht angezeigt wird ( !seen[$3]++dh wenn er zum ersten Mal auf den aktuellen Wert von trifft $3), setzt er den Dateinamen ( f=...)und schreibt den Header in Dateiname ( print h > f). Dann wird die gesamte Zeile an angehängt Dateiname ( print >> f). Es wird Standard FS(Feldtrennzeichen) verwendet: leer . Wenn Sie |als FS(oder sogar als Regex mit gnu awk) verwenden möchten, lesen Sie den Kommentar von cas unten.

don_crissti
quelle
Dieses Skript erstellt auch eine Datei mit dem Namen CODE.txt. Außerdem stimmt der Inhalt der Datei FILE_inputfile_003.txt nicht ganz: NAME | CODE name4 | 003 name5 | 003 NAME | CODE name6 | 003 Es wäre schön, wenn Sie auch Ihr Skript ein wenig erklären würden =)
Kira
1
oder, wenn Sie |als Feldtrennzeichen verwenden möchten : awk -F'|' 'NR==1{h=$0; next} !seen[$2]++{f="FILE_"FILENAME"_"$2".txt";gsub(/ /,"",f);print h > f} {print >> f}'. Das gsub()ist, das Leerzeichen am Anfang von Feld 2 zu entfernen, zB `001` ->001
cas
2
verstanden. awk -F' \\| ' 'NR==1{h=$0; next} !seen[$2]++{f="FILE_"FILENAME"_"$2".txt";print h > f} {print >> f}' danke an stackoverflow.com/questions/25867060/…
cas
1
@cas der FS ist ein regulärer Ausdruck, der den frisst \ . Sie müssen den \ ersten entkommen und dann den entkommenen verwenden \ , um dem zu entkommen |. Zugegeben, nicht sehr intuitiv.
Terdon
1
@terdon - richtig, es heißt dynamischer Regex
don_crissti
1

Ich wette, jemand wird sich einen Einzeiler einfallen lassen, aber ich musste ein Skript erstellen:

in='inputfile'
header=$(head -n1 "$in")
codes=($(sed -n 's/.*| \([0-9]\+\)/\1/p' "$in" | uniq ))
for line in "${codes[@]}"; do
    out="file_$i.txt"
    echo "$header" > "$out"
    grep "|.* $line$" "$in" >> "$out"
done
Kira
quelle
Wenn ich das Skript ausführe, wird folgende Fehlermeldung angezeigt: script.sh: 8: script.sh: i ++: nicht gefunden.
Geralt
Verwenden Sie Bash? Überprüfen Sie die aktualisierte Antwort.
Kira
Ja, #! / Bin / bash
Geralt
Jetzt mit dem i=$((i+1))funktioniert es richtig?
Kira
1
Nun, dieser kam zurück und hat mich gebissen! Du hast ganz recht, ich entschuldige mich. Die Newlines sorgen dafür, dass es funktioniert, obwohl es eine Zeichenfolge ist. Ich habe ein paar Verbesserungen vorgenommen, um meinen Fehler zu büßen und mich die Gegenstimme zurücknehmen zu lassen.
Terdon