2012-08-03 35 views
5

Ich versuche, zwei Zeilen einer Musterübereinstimmung aus einer Datei voller Transaktionen zu löschen. Ie. finde die Übereinstimmung, lösche dann zwei Zeilen davor, lösche dann zwei Zeilen danach und lösche dann die Übereinstimmung. Das schreibe dies zurück in die Originaldatei.Zeilen vor und nach einem Match in bash löschen (mit sed oder awk)?

So die Eingabedaten

D28/10/2011 
T-3.48 
PINITIAL BALANCE 
M 
^ 

und mein Muster ist

sed -i '/PINITIAL BALANCE/,+2d' test.txt 

dies jedoch nur zwei Zeilen nach dem Muster Spiel löscht und dann das Muster Spiel zu löschen. Ich kann keine logische Möglichkeit finden, alle 5 Zeilen von Daten aus der Originaldatei mit sed zu löschen.

Antwort

4

sed wird es tun:

sed '/\n/!N;/\n.*\n/!N;/\n.*\n.*PINITIAL BALANCE/{$d;N;N;d};P;D' 

Es nächste Art und Weise funktioniert:

  • sed, wenn nur eine Saite in Musterraum hat es ein anderes
  • wenn es verbindet nur zwei es verbindet die dritte
  • wenn es natch zu Muster LINE + LINE + LINE mit BALANCE es verbindet zwei folgende Strings, löscht sie und geht bei t er beginnt
  • wenn nicht, ist es die erste Zeichenfolge aus Muster druckt und löscht sie und geht am Anfang, ohne den Musterraum klauen

appearence des Musters auf der ersten Zeichenfolge zu verhindern, sollten Sie das Skript ändern:

sed '1{/PINITIAL BALANCE/{N;N;d}};/\n/!N;/\n.*\n/!N;/\n.*\n.*PINITIAL BALANCE/{$d;N;N;d};P;D' 

Es schlägt jedoch fehl, wenn Sie eine andere PINITIAL BALANCE in Zeichenfolge haben, die gelöscht werden soll.Aber auch andere Lösungen nicht zu =)

1

Für eine solche Aufgabe, würde ich wahrscheinlich erreichen für eine erweiterte Tool wie Perl:

perl -ne 'push @x, $_; 
      if (@x > 4) { 
       if ($x[2] =~ /PINITIAL BALANCE/) { undef @x } 
        else { print shift @x } 
      } 
      } END { print @x' 
+0

Danke für den Vorschlag choroba. Ich habe noch nie in Perl programmiert ... gibt es keine Möglichkeit, dies in bash zu tun? – juliushibert

+0

Wenn es überhaupt möglich ist, ist es auch in bash möglich. Aber es ist die Mühe nicht wert ... – choroba

6

ein awk Einzeiler den Job kann tun:

awk '/PINITIAL BALANCE/{for(x=NR-2;x<=NR+2;x++)d[x];}{a[NR]=$0}END{for(i=1;i<=NR;i++)if(!(i in d))print a[i]}' file 

Test:

kent$ cat file 
###### 
foo 
D28/10/2011 
T-3.48 
PINITIAL BALANCE 
M 
x 
bar 
###### 
this line will be kept 
here 
comes 
PINITIAL BALANCE 
again 
blah 
this line will be kept too 
######## 

kent$ awk '/PINITIAL BALANCE/{for(x=NR-2;x<=NR+2;x++)d[x];}{a[NR]=$0}END{for(i=1;i<=NR;i++)if(!(i in d))print a[i]}' file 
###### 
foo 
bar 
###### 
this line will be kept 
this line will be kept too 
######## 

Erklärung hinzufügen

awk '/PINITIAL BALANCE/{for(x=NR-2;x<=NR+2;x++)d[x];} #if match found, add the line and +- 2 lines' line number in an array "d" 
     {a[NR]=$0} # save all lines in an array with line number as index 
     END{for(i=1;i<=NR;i++)if(!(i in d))print a[i]}' #finally print only those index not in array "d" 
    file # your input file 
+0

Danke für den awk oneliner Kent. Das sieht sehr komplex aus. Es wäre toll, wenn du eine kleine Erklärung anbieten könntest? – juliushibert

+0

@juliushibert kurze Erklärung hinzugefügt – Kent

+0

elegante Lösung –

0

diesen Code in eine grep.sed

H 
s:.*:: 
x 
s:^\n:: 
:r 
/PINITIAL BALANCE/ { 
    N 
    N 
    d  
} 

/.*\n.*\n/ { 
    P 
    D 
} 
x 
d 

Datei speichern und einen Befehl wie folgt ausführen:

`sed -i -f grep.sed FILE` 

Sie können es verwenden, um entweder:

sed -i 'H;s:.*::;x;s:^\n::;:r;/PINITIAL BALANCE/{N;N;d;};/.*\n.*\n/{P;D;};x;d' FILE 
+0

Fügen Sie '$ d' vor' N; N; d' innerhalb '/ bar /' Block und es funktioniert gut, wenn die 'bar'-Zeichenfolge die letzte ist. – rush

1

Dies könnte für Sie funktionieren (GNU sed):

sed ':a;$q;N;s/\n/&/2;Ta;/\nPINITIAL BALANCE$/!{P;D};$q;N;$q;N;d' file 
Verwandte Themen