2012-08-03 2 views
5

Sto tentando di eliminare due righe su entrambi i lati di una corrispondenza di modello da un file pieno di transazioni. Vale a dire. trova la corrispondenza, quindi elimina due righe prima di essa, quindi elimina due righe dopo di essa e quindi elimina la corrispondenza. Scrivi questo al file originale.Elimina righe prima e dopo una partita in bash (con sed o awk)?

Così i dati di input è

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

e il mio modello è

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

Tuttavia, questo è l'eliminazione solo due righe dopo il match modello e poi cancellando il pattern match. Non riesco a trovare un modo logico per cancellare tutte le 5 righe di dati dal file originale usando sed.

risposta

4

sed lo farà:

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

Funziona modo seguente:

  • se sed ha una sola corda nello spazio modello si unisce un altro
  • se ci sono solo due si unisce al terzo
  • se fa natch per modellare LINE + LINE + LINE con BALANCE si unisce a due stringhe seguenti, le elimina e va a t ha inizio
  • in caso contrario, si stampa la prima stringa da modello e lo elimina e va all'inizio, senza strisciare lo spazio modello

per prevenire comparsa di modello sulla prima corda si dovrebbe modificare lo script:

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

Tuttavia non riesce nel caso in cui si dispone di un altro PINITIAL BALANCE nella stringa che verranno eliminati.Tuttavia altre soluzioni non riesce troppo =)

1

Per un tale compito, probabilmente raggiungere per uno strumento più avanzato come il Perl:

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

Grazie per il suggerimento choroba. Non ho mai programmato in Perl prima ... non c'è modo di farlo in bash? – juliushibert

+0

Se è possibile, è possibile anche in bash. Ma non ne vale la pena ... – choroba

6

un awk one-liner può fare il lavoro:

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 

prova:

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 
######## 

aggiungere qualche spiegazione

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

Grazie per l'awk oneliner Kent. Sembra molto complesso. Sarebbe fantastico se potessi offrire una piccola spiegazione? – juliushibert

+0

@juliushibert spiegazione breve aggiunta – Kent

+0

soluzione elegante –

0

salvare questo codice in un file grep.sed

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

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

ed eseguire un comando come questo:

`sed -i -f grep.sed FILE` 

si può utilizzare quindi o:

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

Aggiungere '$ d' prima di' N; N; d' all'interno di '/ bar /' block e funzionerà perfettamente nel caso in cui la stringa 'bar' sia l'ultima. – rush

1

Questo potrebbe funzionare per voi (GNU sed):

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