staging.inyokaproject.org

Für diese Funktion musst du eingeloggt sein.

In Textdatei Zeilen zwischen 2 Suchworten löschen

Status: Ungelöst | Ubuntu-Version: Ubuntu 16.04 (Xenial Xerus)
Antworten |

jaybee

Anmeldungsdatum:
18. Dezember 2018

Beiträge: 5

Hallo, ich habe eine Textdatei die folgendermaßen aufgebaut ist:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
schlüsselwort hase {
blah
blubb
tada
};

schlüsselwort igel {
blahdfg
trzrtz
blubb
tadasdf
};

schlüsselwort maus {
blah
blubbsdfsd
tada

 };
schlüsselwort wurm {
blah
blubb
trre
tada
};
etc.

Ich möchte nun nach Schlüsselworten suchen können und dann alle Zeilen bis zum nächsten Schlüsselwort rauslöschen. Also wenn ich zb nach igel suche, sollen die Zeilen

1
2
3
4
5
6
schlüsselwort igel {
blahdfg
trzrtz
blubb
tadasdf
};

aus der Datei gelöscht werden. Wenn ich nach maus suche, sollen danach die Zeilen

1
2
3
4
5
6
schlüsselwort maus {
blah
blubbsdfsd
tada

 };

fehlen.

Wie gehe ich das am besten an? Ob Bash, Perl, Python, PHP, oder Irgendwas ist mir egal. Es soll nur auf der Kommandozeile bzw aus einem Bashscript heraus aufrufbar sein.

danke Jay

Vain

Avatar von Vain

Anmeldungsdatum:
12. April 2008

Beiträge: 2510

Moin,

das kannst du mit sed und einer Range relativ einfach machen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
sed '/^schlüsselwort igel {$/,/};$/d' <deine_datei
      └──────────┬─────────┘   └┬┘ │
                 │              │  └ Aktion, die innerhalb der Range
                 │              │    ausgeführt werden soll: Lösche die
                 │              │    Zeile.
                 │              │
                 │              └ Ende der Range: Eine Zeile, die auf
                 │                „};“ endet.
                 │
                 └ Anfang der Range: Eine Zeile, die genau mit
                   „schlüsselwort igel {“ übereinstimmt (keine
                   Leerzeichen an Anfang oder Ende).

Wenn du damit zufrieden bist und die Datei direkt inplace bearbeiten willst, dann nimm sed -i ... deine_datei.

(Ich bin jetzt davon ausgegangen, dass du nicht tatsächlich alle Zeilen bis zum nächsten Schlüsselwort löschen willst, sondern eben den Block, der nach deinem Schlüsselwort folgt.)

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11278

Nicht unbedingt eleganter als die Lösung mit sed (solange ein Block zuverlässig von Zeilenumbrüchen begrenzt wird), aber prinzipiell könnte man das auch mit einem regulären Ausdruck und einer Ersetzung lösen:

perl -0pe -i 's/schlüsselwort\s+igel\s+\{.*?};//smg' test.txt 

Das liest die Datei in einem Rutsch ein, sucht nach dem Ausdruck ohne Berücksichtigung der Zeilengrenzen und ersetzt alle Treffer durch einen leeren String.

jaybee

(Themenstarter)

Anmeldungsdatum:
18. Dezember 2018

Beiträge: 5

Hallo, erstmal Danke für die schnellen Tipps.

Ich bin jetzt davon ausgegangen, dass du nicht tatsächlich alle Zeilen bis zum nächsten Schlüsselwort löschen willst

Jein. Zwischen den Blöcken befinden sich jeweils noch zwischen 0 und x Leerzeilen, die auch mit weg sollen.

d.h. es sollen alle Zeilen von der Zeile mit "schlüsselwort igel" bis zu der Zeile vor dem nächsten Auftreten von "schlüsselwort" gelöscht werden. Die Blöcke sind dazu auch unterschiedlich lang.

solange ein Block zuverlässig von Zeilenumbrüchen begrenzt wird ... sucht nach dem Ausdruck ohne Berücksichtigung der Zeilengrenzen

Was bedeutet das? In einem Block kommen mehrere Zeilen, Leerzeilen, Kommentarzeilen etc. vor.

Jay

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11278

jaybee schrieb:

solange ein Block zuverlässig von Zeilenumbrüchen begrenzt wird ... sucht nach dem Ausdruck ohne Berücksichtigung der Zeilengrenzen

Was bedeutet das? In einem Block kommen mehrere Zeilen, Leerzeilen, Kommentarzeilen etc.

Es gibt ja Sprachen bzw. Dateiformate, bei der Zeilenumbrüche und überflüssiger Whitespace syntaktisch weitgehend egal sind und Ausdrücke z.B. mit einem Semikolon abgeschlossen werden - einem C++ Compiler ist es egal, ob du alles in eine Zeile schreibst oder das schön leserlich umbrichst. Statt

1
2
3
4
5
6
7
8
9
struct foo {
  int bar;
  int spam;
};

struct ham {
  int blah;
  int fasel;
};

könnte da auch sowas stehen (schlechter lesbarer Stil, aber äquivalent):

1
struct foo {int bar; int spam;}; struct ham {int blah; int fasel;};

sed arbeitet Dateien immer zeilenweise ab und würde dann gleich die ganze Zeile löschen, wenn man wie im Beispiel von Vain vorgehen würde:

1
sed '/^struct foo {$/,/};$/d' < test.cpp

während der Ansatz mit dem Regulären Ausdruck

perl -0pe 's/struct\s+foo\s*\{.*?};//smg' test.cpp 

in dem Fall nur den gewünschten Struct löscht (natürlich ist es nicht besonders zuverlässig beliebigen C++ Code mit Regulären Ausdrücken zu parsen, dafür würde man normalerweise einen Lexer nutzen). jaybee schrieb:

Jein. Zwischen den Blöcken befinden sich jeweils noch zwischen 0 und x Leerzeilen, die auch mit weg sollen.

Das Löschen leerer Zeilen ist mit sed schnell erledigt:

sed -i '/^$/d' datei 

jaybee

(Themenstarter)

Anmeldungsdatum:
18. Dezember 2018

Beiträge: 5

Es steht nie mehr als ein Block in einer Zeile. Die Datei ist immer so strukturiert wie im Beispiel.

Was ist ein Lexer?

Und: Es sollen nicht einfach alle Leerzeilen aus der Datei gelöscht werden, sondern nur die zwischen gelöschtem Block und dem Anfang des nächsten Blockes.

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11278

jaybee schrieb:

Es steht nie mehr als ein Block in einer Zeile. Die Datei ist immer so strukturiert wie im Beispiel.

Ok, das vereinfacht die Sache etwas - kann in den Blöcken die Zeichenkette }; oder schlüsselwort vorkommen?

Was ist ein Lexer?

Ein lexikalischer Scanner aka Tokenizer - der ist stark von der Syntax der genutzten Sprache abhängig, also müsste man wissen, welche Zeichen außer geschweiften Klammern da Blöcke, Kommentare, Strings, Datenstrukturen umschließen, wie die Escape-Zeichen aussehen usw.

Und: Es sollen nicht einfach alle Leerzeilen aus der Datei gelöscht werden, sondern nur die zwischen gelöschtem Block und dem Anfang des nächsten Blockes.

Ok, dann könnte man sowas machen, wenn das Schlüsselwort nicht innerhalb der Blöcke auftaucht:

perl -0pe -i 's/(?<=};).*?(?=schlüsselwort)/\n/smg' test.txt 

Kurz: Lese die Datei in einem Rusch ein und behandle sie wie einen großen String, ein Punkt matcht auch Newlines. Finde die kürzeste Zeichenkette zwischen }; und schlüsselwort und ersetze sie durch \n. Schreibe das Ergebnis in die Datei (sonst das -i weglassen).

jaybee

(Themenstarter)

Anmeldungsdatum:
18. Dezember 2018

Beiträge: 5

seahawk1986 schrieb:

kann in den Blöcken die Zeichenkette }; oder schlüsselwort vorkommen?

Die Zeichenkette }; kommt nur am Ende des Blockes, aber vor den eventuellen zu löschenden Leerzeilen vor.

Die Zeichenkette "schlüsselwort" kann auch innerhalb des Blockes mehrmals vorkommen. Die Vorkommen innerhalb {}; sollen aber ignoriert werden.

Der Suchbegriff der das Löschen triggern soll, kommt ebenfalls auch innerhalb der {}; vor.

"schlüsselwort" als Trennzeichen und der danach folgende Suchbegriff stehen immer außerhalb {};

Jay

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11278

Gibt es geschweifte Klammern innerhalb der Blöcke? Falls ja: kommen die auch in mit einfachen oder doppelten Anführungszeichen umschlossenen Strings oder in escapter From (also z.B. \{ bzw. \}) vor?

jaybee

(Themenstarter)

Anmeldungsdatum:
18. Dezember 2018

Beiträge: 5

Sorry, ich hatte wohl die Benachrichtigung übersehen. Nein, geschweifte Klammern kommen nur am Anfang und Ende des Blockes vor.

Antworten |