SED: Löschen Sie 4 Zeilen über und unter 5 Zeilen nach dem Mustervergleich

7

Ich habe eine Hugh-Datei mit folgenden Details:

define host{
        use                     generic-host            ; Name of host template to use
        host_name               ServerA_172.29.16.102
        alias                   ServerA_172.29.16.102
        address                 172.29.16.102
        check_command           check-host-alive
        max_check_attempts      3
        notification_interval   120
        notification_period     24x7
        }



define host{
        use                     generic-host            ; Name of host template to use
        host_name               ServerB_172.29.16.103
        alias                   ServerB_172.29.16.103
        address                 172.29.16.103
        check_command           check-host-alive
        max_check_attempts      3
        notification_interval   120
        notification_period     24x7
        }

Was ich will, suche "address 172.29.16.102"und lösche 4 Zeilen über und nach 5 Zeilen.

Ich habe versucht, mit sed zu folgen, aber nicht zu arbeiten

sed '$N;$N;N;/address                 172.29.16.102/,+5d' hosts
Rahul Patil
quelle
MUSS es sed sein, oder sind Sie offen für andere Werkzeuge?
Ghoti
Wenn Sie beide 4 Zeilen vor UND 5 nach löschen , sed -n '/\s*address/p;' filewird es sicher gelöst? Welches ist das gleiche wie grep 'address' file? Oder fehlt mir hier etwas? Wie soll die Ausgabe aussehen?
Drav Sloan
Da es viele Einträge in der Eingabedatei gibt, möchte ich nur einige Container-Mittel entfernen, um define {zu ermitteln, }ob diese Adresse übereinstimmt. Es sollte kein anderer Inhalt entfernt werden, das war's
Rahul Patil,

Antworten:

7

Wenn jeder define_hostAbschnitt durch einen oder mehrere Zeilenumbrüche getrennt ist, dann ist dies genau die Art von Problem des GNU awk mehrzeilige Rekord Unterstützung soll lösen

awk -v RS= '!/172.29.16.102/{printf $0""RT}'
iruvar
quelle
Ich werde dies akzeptieren und können Sie es bitte erklären. danke ..
Rahul Patil
Dies funktioniert auch nicht, sehr seltsame Dinge passieren, dies entfernt auch andere Einträge, die nicht verwandt sind ..
Rahul Patil
@ RahulPatil, interessant. Hat dieser andere Eintrag zufällig enthalten 172.29.16.102? War es auch durch Leerzeilen klar von anderen Einträgen getrennt?
iruvar
Nein, da hat das nichts damit zu tun
Rahul Patil
4

Wann immer ich diese Art von Frage sehe, sagt mir mein Bauch, dass dies ein Job für ist grep. Die grepFähigkeit, -vdie Ergebnisse bei Verwendung der Vorher- und Nachher-Schalter ( -B ..& -A ..) umzukehren ( ), lässt dies jedoch nicht zu.

Doch dieser clevere Ansatz des Aufrufs grep2 mal macht es viel sauberer als eine der awkoder sedLösungen , die ich bisher gesehen habe.

$ grep -v "$(grep -B 4 -A 5 'address 172.29.16.102' <file>)" <file>

Beispiel

Hier sind einige Beispieldaten.

$ cat sample.txt
define host{
        use                     generic-host            ; Name of host template to use
        host_name               ServerA_172.29.16.102
        alias                   ServerA_172.29.16.102
        address                 172.29.16.102
        check_command           check-host-alive
        max_check_attempts      3
        notification_interval   120
        notification_period     24x7
        }

line1b
line2b
line3b
line4b
address 172.29.16.102
line5a
line4a
line3a
line2a
line1a

define host{
        use                     generic-host            ; Name of host template to use
        host_name               ServerB_172.29.16.103
        alias                   ServerB_172.29.16.103
        address                 172.29.16.103
        check_command           check-host-alive
        max_check_attempts      3
        notification_interval   120
        notification_period     24x7
        }

Nun, wenn wir unseren Befehl ausführen:

$ grep -v "$(grep -B 4 -A 5 'address 172.29.16.102' sample.txt)" sample.txt
define host{
        use                     generic-host            ; Name of host template to use
        host_name               ServerA_172.29.16.102
        alias                   ServerA_172.29.16.102
        address                 172.29.16.102
        check_command           check-host-alive
        max_check_attempts      3
        notification_interval   120
        notification_period     24x7
        }


define host{
        use                     generic-host            ; Name of host template to use
        host_name               ServerB_172.29.16.103
        alias                   ServerB_172.29.16.103
        address                 172.29.16.103
        check_command           check-host-alive
        max_check_attempts      3
        notification_interval   120
        notification_period     24x7
        }
slm
quelle
Dies funktioniert gut für das OP; Im allgemeinen Fall tritt jedoch ein Problem auf, wenn die übereinstimmenden Zeilen reservierte Regex-Zeichen enthalten.
Andrew Hows
Dies funktioniert auch nicht in dem allgemeinen Fall, in dem die zu entfernenden Zeilen vor und nach der übereinstimmenden Zeile in der Datei nicht eindeutig sind und die anderen Kopien nicht entfernt werden sollten.
Carandraug
2

Anstatt eine strenge Anzahl von Zeilen mit sed oder grep zu verwenden, würde ich lieber etwas schreiben, das eine grundlegende Interpretation des Dateiformats selbst vornimmt und den Inhalt jedes Datensatzes bewertet . Betrachten Sie Folgendes mit awk:

#!/usr/bin/awk -f

function doit(blob) {
  if (blob !~ /address[[:space:]]+172\.29\.16\.102/) {
    print blob;
  } 
}

# Find a new record...
/^define host/ {
  doit(blob);
  blob="";
}

# Don't start each record with a blank line...
length(blob) { blob=blob "\n"; }

# Collect our data...    
{ blob=blob $0; }

END {
  doit(blob);
}

Die Idee hier ist, dass wir durch die Datei gehen und jedes Mal, wenn wir sehen define host, jede Zeile zur Variablen hinzufügen blob. Wenn ein neuer Datensatz beginnt oder wenn wir am Ende der Datei angelangt sind, verarbeiten wir diese Variable mithilfe der doit()Funktion.

Dies funktioniert sowohl in GNU als auch in Classic Awk.

Ghoti
quelle
2

Dies ist ein perfektes Beispiel dafür , warum sedist der s tream ed itor, und wird nie die Kraft ersetzt exfür in-Place - Datei bearbeiten.

ex -c '/address  *172.29.16.103/
?{?,/}/d
x' input

Dieser Befehl ist eine vereinfachte Form, die nicht so robust ist, wie sie sein könnte, sondern zur Veranschaulichung dient.

Der erste Befehl findet den angegebenen regulären Ausdruck und bewegt den Cursor in diese Zeile.

Der zweite Befehl besteht aus zwei durch Komma getrennten Adressen, auf denen der dBefehl elete ausgeführt wird. ?{?Sucht von der aktuellen Zeile rückwärts nach einer offenen geschweiften Klammer und /}/von der aktuellen Zeile nach einer engen geschweiften Klammer vorwärts. Alles dazwischen wird gelöscht (zeilenweise, sodass auch der Anfang der offenen geschweiften Klammer gelöscht wird).

xspeichert die Änderungen und beendet. Und inputist natürlich der Name der Datei.

Für die von Ihnen eingegebene Eingabe funktioniert dieser Befehl genau wie gewünscht.


Nun erwähnte ich, dass dies erheblich verbessert werden könnte. Wir werden mit dem regulären Ausdruck beginnen. Das offensichtlichste Ergebnis hierbei ist, dass Perioden Platzhalter sind. Der angegebene reguläre Ausdruck könnte auch mit "172329-16 103" übereinstimmen. Daher müssen die Punkte mit umgekehrten Schrägstrichen versehen werden, damit sie nur mit wörtlichen Punkten übereinstimmen.

Als nächstes kommt das Leerzeichen. Ich habe zwei Leerzeichen gefolgt von einem * eingefügt (ich hätte verwenden können, \+aber ich weiß nicht, ob diese Funktion in POSIX erforderlich ist), aber was ist, wenn die Datei Tabulatoren enthält? Die beste Lösung ist zu verwenden [[:space:]]. (Das würde viel besser aussehen \+; wenn jemand herausfindet, ob das POSIX ist, schreibe bitte einen Kommentar.)

Was ist, wenn der reguläre Ausdruck nicht in der Datei gefunden wird? Nun, dann wird die Datei einfach zum Bearbeiten geöffnet und der Befehl "Suchen" schlägt fehl, und eine Fehlermeldung wird gedruckt und der Rest der angegebenen Befehle wird nicht ausgeführt - Sie bleiben im exEditor So können Sie Änderungen von Hand vornehmen. Wenn Sie jedoch die Änderungen in einem Skript automatisieren wollen, möchten Sie wahrscheinlich den Editor Ausgang , wenn keine Änderungen vorgenommen werden müssen. Die Antwort besteht darin, den gBefehl lobal zu verwenden und das -sFlag zu verwenden, um die Ausgabe von ex:

ex -sc 'g/address[[:space:]][[:space:]]*172\.29\.16\.103/ ?{?,/}/d
x' input

Dies entspricht nicht ganz dem früheren Befehl. Wenn es mehr als einen geschweiften Klammerblock mit einer übereinstimmenden Linie gibt, löscht der globale Befehl hier alle. Das ist wahrscheinlich was du sowieso willst.

Wenn Sie beim Beenden nur die erste Übereinstimmung löschen möchten, ohne die Datei zu ändern, wenn überhaupt keine Übereinstimmungen vorhanden sind, können Sie den xBefehl als Teil des Arguments für den gBefehl verwenden (um die Datei zu beenden, nachdem der erste Löschbefehl ausgeführt wurde). und geben Sie q!unten einen Befehl ein, falls der gBefehl mangels übereinstimmender Zeilen nicht ausgeführt wird.

ex -sc 'g/address[[:space:]][[:space:]]*172\.29\.16\.103/ ?{?,/}/d | x
q!' input

Um ehrlich zu sein, lassen diese Befehle den Prozess viel komplizierter aussehen als er ist. Die Robustheit geht zu Lasten der extremen Klarheit und Lesbarkeit des Codes. Es ist ein Kompromiss.

Ich empfehle, einige Dateien interaktiv exzu bearbeiten , um ein Gefühl dafür zu bekommen. Auf diese Weise können Sie sehen, was Sie tun. Eine solche Bearbeitungssitzung ex, um dieses Update interaktiv durchzuführen, sieht ungefähr so ​​aus:

$ ex input
"input" 23L, 843C
Entering Ex mode.  Type "visual" to go to Normal mode.
:/103
        host_name               ServerB_172.29.16.103
:?{?,/}/d                             # This deletes the current block

:$p                                   # Print and move to last line

:-5,.p                                # Print some more lines to check result
        notification_interval   120
        notification_period     24x7
        }



:?}?+,.d                              # Trim whitespace
        }
:x                                    # Save and exit
$ 

Die POSIX-Spezifikationen zurex weiteren Lektüre.

Platzhalter
quelle
0

Was ist damit?

egrep -v '^([[:space:]]+(use|host_name|alias|check_command|max_check_attempts|notification_interval|notification_period)[[:space:]]+|^define host{|^[[:space:]]+})'

Es entfernt die bestimmten Zeilen, die nicht benötigt werden, wenn wir annehmen, dass es keine ähnlichen Zeilen in der Datei gibt, die wir nicht entfernen sollten.

Strahl
quelle
0

Mit können sedSie tun:

sed -ne'/^[^ ]/!{H;$!d;}' <in >out \
    -e 'x;//{/\n *address  *172\.29\.16\.103/!p;}'

... die jeden Block beim HEinlesen separat im alten Speicherplatz puffern und nur diejenigen drucken, die keine Übereinstimmung für Ihr Muster enthalten.

mikeserv
quelle