Ich habe folgende Textdatei:
#unimportant comment
#possible more unimportant comments
#info1 info2 info3 ,importantname1
importanttext1
#info1 info2 info3 ,importantname2
importanttext2
#info1 info2 info3 ,importantname3
importanttext3
Ich möchte jede Datei in einzelne Dateien aufteilen. Ich muss lediglich die nicht kommentierten URLs extrahieren, das Beibehalten von Kommentaren ist optional. Ich möchte, dass jede Datei einen Namen wie importantname1.txt oder den Namen nach dem Komma am Ende jeder Kommentarzeile erhält, angehängt mit .txt.
also hätte importantname1.txt folgenden Inhalt:
importanttext1
oder möglicherweise
#info1 info2 info3 ,importantname1
importanttext1
Die Zeile würde also extrahiert und mit dem Dateinamen nach dem Kommentar gespeichert und mit .txt angehängt, in diesem Fall Dateiname importantname1.txt
Ich muss dies für jeden Zeilensatz in der Beispieldatei tun. Das Beibehalten der Kommentare ist unwichtig, aber ich brauche es, damit es skriptfähig ist. Ich muss auch eine unbekannte Anzahl von Kommentarzeilen im Header berücksichtigen. Die Kommentarzeile wird immer vor jeder importanttextX-Zeile stehen.
Antwort1
Versuchen:
awk -F, '/^#/{f=$NF".txt";cmt=$0; next} {printf "%s\n%s\n",cmt,$0 >f; close(f)}' file
Beispiel
Auf Ihre Beispieleingabe angewendet:
$ awk -F, '/^#/{f=$NF".txt";cmt=$0; next} {printf "%s\n%s\n",cmt,$0 >f; close(f)}' file
Nachdem das obige ausgeführt wurde, befinden sich die folgenden Dateien im Verzeichnis:
$ ls
file importantname1.txt importantname2.txt importantname3.txt
Der Inhalt der neuen Dateien ist:
$ cat importantname1.txt
#info1 info2 info3 ,importantname1
importanttext1
$ cat importantname2.txt
#info1 info2 info3 ,importantname2
importanttext2
$ cat importantname3.txt
#info1 info2 info3 ,importantname3
importanttext3
Wie es funktioniert
Awk liest die Eingabedatei Zeile für Zeile durch. Unser Skript klassifiziert diese Zeilen als Kommentare oder Nicht-Kommentare. Bei Kommentarzeilen werden Dateiname und Kommentar gespeichert. Bei Nicht-Kommentaren wird eine neue Datei erstellt und ausgedruckt.
`-F,
Dies weist awk an, bei der Eingabe ein Komma als Feldtrennzeichen zu verwenden. Auf diese Weise ist der Dateiname immer das letzte Feld.
/^#/{f=$NF".txt";cmt=$0; next}
Wenn eine Zeile mit beginnt
#
, speichern wir das letzte Feld,$NF
, plus.txt
als Dateinamenf
. Die gesamte Kommentarzeile wird als gespeichertcmt
. Anschließend weisen wir awk an, den Rest der Befehle zu überspringen und direkt von vorne in dernext
Zeile zu beginnen.printf "%s\n%s\n",cmt,$0 >f; close(f)
Bei Zeilen ohne Kommentar drucken wir den zuletzt gesehenen Kommentar,
cmt
, und die aktuelle Zeile,$0
, in den zuletzt gesehenen Dateinamenf
. Anschließend schließen wir den Datei-Handle fürf
.
Schutz vor fehlerhaften Dateinamen
Wenn die Felder, die als Dateinamen verwendet werden sollen, enthalten /
, interpretiert das Betriebssystem die Dateinamen so, als ob sie Verzeichnisse enthalten. Um das zu vermeiden, können wir all /
durch -
using gsub(/\//, "-", f)
wie folgt ersetzen:
awk -F, '/^#/{f=$NF".txt";gsub(/\//, "-", f); cmt=$0; next} {printf "%s\n%s\n",cmt,$0 >f; close(f)}' file
Antwort2
Eine Kombination aus grep
und csplit
könnte die Aufgabe erledigen, indem a) grep
alle nicht auskommentierten Zeilen sowie die vorangehende Infozeile angepingt werden und b) die Ausgabe basierend auf der Infokommentarzeile aufgeteilt wird:
grep -v -B1 '^#' file | csplit -z - '/^#/' '{*}'
D. h., extrahieren Sie keine -v
Zeilen, die am Anfang ein # haben, ^#
sondern eine Zeile davor -B1
. Teilen Sie dann den eingehenden Pipe-Input -
bei jedem # am Zeilenanfang auf, ignorieren Sie leere Dateien -z
und wiederholen Sie dies so oft wie möglich {*}
.
Das Umbenennen müsste ein separater Schritt sein ( csplit
benennt die Ausgabe automatisch als xx00, xx01 ... - ändert Prä- und Suffix jeweils mit den Optionen -f
und -b
).
#/bin/bash
for f in xx* ; do
mv "$f" "$( sed -n '2p' "$f" )".txt
done