2012-06-09 31 views
5

Necesito eliminar líneas de un fichero que son después pattern1 y entre el patrón 2 y Pattern3, como a continuación:Eliminar Líneas: después pattern1 y entre patrón2 y Pattern3 usando awk/sed/Perl

aaaaaaaa 
bbbbbbbb 
pattern1 <-----After this line 
cdededed 
ddededed 
pattern2 
fefefefe <-----Delete this line 
efefefef <-----Delete this line 
pattern3 
adsffdsd 
huaserew 

Por favor, puede sugerir cómo se puede hacer usando awk o sed o en perl.

+1

Si ** ** pattern1 se produce después de ** ** patrón2 debe producirse la eliminación? –

Respuesta

4
sed '/pattern1/,${ /pattern2/,/pattern3/{/pattern2/b; /pattern3/b; d;} };' file 

con formato:

/pattern1/,$ { 
    /pattern2/,/pattern3/ { 
     /pattern2/b; 
     /pattern3/b; 
     d; 
    } 
} 

Explicación:

  • /pattern1/,$ es el rango de líneas después de pattern1 al final del archivo
  • /pattern2/,/pattern3/ es la gama de líneas entre pattern2 y pattern3
  • /pattern2/b; y /pattern3/b; salta el pattern2 y pattern3 líneas que están incluidos de otra forma en el rango (ver the sed faq)
  • d elimina las otras líneas en el rango

actualización

De los comentarios, el bloque interno se puede reescribir:

//!d 

donde:

  • // (un patrón vacío) coincide con el utilizado por última vez de expresiones regulares (que en este caso es a la vez pattern2 y pattern3
  • ! invierte el siguiente comando para que se aplique a todo lo excepto líneas coincida con el patrón
  • d elimina estas líneas

Así que el patrón completo, es reescrito:

/pattern1/,$ { 
    /pattern2/,/pattern3/ { 
     //!d 
    } 
} 
+0

+1 pero es una lástima que tenga que repetir los patrones. –

+2

Estoy de acuerdo; es una limitación de sed. Realmente no hay una mejor manera de hacer esto (en sed). awk y perl seguramente tienen soluciones más elegantes. – beerbajay

+2

@glennjackman: No necesita repetir el patrón: 'sed '/ pattern1 /, $ {/ pattern2 /,/pattern3/{// b; d;}}; '' –

3

uso awk como una máquina de estados:

awk ' 
    BEGIN {print_line = 1} 
    /pattern1/ {consider = 1} 
    consider && /pattern2/ {print_line = 0; print} 
    consider && /pattern3/ {print_line = 1} 
    print_line {print} 
' filename 
1

Finalización de la piedra de Rosetta:

perl -ne '++$saw_pattern1 if /pattern1/; 
      $inside = ($saw_pattern1 && /pattern2/) .. /pattern3/; 
      print unless $inside && ($inside > 1 && $inside !~ /E0$/)' \ 
    input 

El código se aprovecha de Perl de .. range operator.

En contexto escalar, .. devuelve un valor booleano. El operador es biestable, como un flip-flop, y emula el operador de rango de línea (coma) sed, awk y varios editores.Cada operador .. mantiene su propio estado booleano, incluso en llamadas a una subrutina que lo contiene. Es falso siempre que su operando izquierdo sea falso. Una vez que el operando de la izquierda es cierto, el operador de rango se mantiene fiel hasta que el operando de la derecha es cierto, DESPUÉS cual el operador de rango se convierte en falsa de nuevo. No se convierte en falsa hasta la próxima vez que se evalúa el operador de rango ...

El operando de la derecha no se evalúa mientras que el operador está en estado falso, y el operando de la izquierda no se evalúa mientras que el operador está en el estado verdadero . La prioridad es un poco menor que || y &&. El valor devuelto es la cadena vacía para falso o un número de secuencia (que comienza con 1) para verdadero. El número de secuencia se restablece para cada rango encontrado. El número de secuencia final en un rango tiene la cadena E0 anexado a él, que no afecta a su valor numérico, pero le da algo para buscar si desea excluir el punto final. Puede excluir el punto empezando por la espera del número de secuencia a ser mayor que 1.

+0

Actualicé esto con una [respuesta perl mucho más concisa] (http://stackoverflow.com/a/10961632/124486). –

1

Esto podría funcionar para usted:

sed '/pattern1/,$!b;/pattern2/,/pattern3/!b;//!d' file 
2

Si usted está buscando una solución rápida en la línea de comandos usando Perl, este es un caso ideal para el operador flip-flop. Ahora, hay dos maneras, que esta pregunta se puede interpretar de casos extremos - ambos funcionarán de la misma, siempre y cuando pattern1 viene antes pattern2:

  1. Si pattern1 viene después patrón2 pero antes Pattern3 borrar todo en el medio pattern1 y Pattern3

  2. o, si pa ttern1 viene después patrón2 pero antes Pattern3 no hacen nada menos que ves otra pattern1.

Antes de empezar, tome nota del argumento del Perl -p

-n    assume "while (<>) { ... }" loop around program 
-p    assume loop like -n but print line also, like sed 

Ahora, para el primero, te doy ..

perl -pe'$x ||= /7/; $_= "" if /5/ .. /8/ and $x' <(seq 1 10) 
1 
2 
3 
4 
5 
6 
9 
10 

$x ||= /7/: Esto establece $x a la valor de /7/ regresar cuando $x es false. /7/ devolverá true cuando coincida. Esto significa $x consigue el sistema de verdad, en el primer partido y la naturaleza de ||= es no volver a poner la variable cuando ya es cierto.

A continuación, establece $_ = '' si el rango es entre /5/ y /8/ y ya se ha puesto a $x cierto. Recordar el camino de cortocircuito obras: a && b medios corren b sólo si a evalúa a true.En este caso, el mero hecho de evaluar a establecerá el estado del operador de flip-flop; eso es lo que queremos; sin embargo, solo queremos que ocurra $_ = '' si ya se ha visto 7.

Ahora, a la segunda interpretación de la quesiton acaba de cambiar el orden ...

perl -pe'$x ||= /7/; $_= "" if $x and /5/ .. /8/' <(seq 1 10) 

Esto imprimirá toda la gama. Perl no comenzará a buscar /5/ hasta que encuentre /7/. En nuestro rango secuencial eso no sucederá.

Por cierto, realmente poner algunas de estas respuestas a la vergüenza, muchos de los espacios no son necesarios ...

perl -pe'$x||=/2/;$_=""if$x&&/5/../8/' # secksey 
+1

No interpreto la pregunta de ninguna manera. La pregunta indica claramente que pattern1 viene * before * pattern2. –

+0

@DennisWilliamson ** ambos ** manejan bien esa maleta. –

+0

@DennisWilliamson Todavía no veo que 'pattern1' venga antes de' pattern2' como evidente en la pregunta, el ejemplo lo tiene pero puede ser casualidad. ** Necesito eliminar líneas de un archivo que están después del patrón1 y entre el patrón 2 y el patrón3 ** ¿por qué no puede 'pattern1' estar entre' pattern2' y 'pattern3'? estableciendo el rango de borrado de 'patrón1' a' patrón3' –

Cuestiones relacionadas