2012-02-25 5 views
8
sed -e '/XXXX/,+4d' fv.out 

Tengo que encontrar un patrón particular en un archivo y eliminar 5 líneas arriba y 4 líneas debajo de él simultáneamente. Descubrí que la línea anterior elimina la línea que contiene el patrón y cuatro líneas debajo de ella.Eliminar n1 líneas anteriores y n2 líneas siguientes con respecto a una línea que contiene un patrón

sed -e '/XXXX/,~5d' fv.out 

In sed manual se da que ~ representa las líneas seguidas por el patrón. Pero cuando lo intenté, fueron las líneas que seguían el patrón que se borraron.

Entonces, ¿cómo elimino 5 líneas arriba y 4 líneas debajo de una línea que contiene el patrón al mismo tiempo?

Respuesta

5

Una forma usando sed, suponiendo que los patrones no están lo suficientemente cerca entre sí:

contenido de script.sed:

## If line doesn't match the pattern... 
/pattern/ ! { 

    ## Append line to 'hold space'. 
    H 

    ## Copy content of 'hold space' to 'pattern space' to work with it. 
    g 

    ## If there are more than 5 lines saved, print and remove the first 
    ## one. It's like a FIFO. 
    /\(\n[^\n]*\)\{6\}/ { 

     ## Delete the first '\n' automatically added by previous 'H' command. 
     s/^\n// 
     ## Print until first '\n'. 
     P 
     ## Delete data printed just before. 
     s/[^\n]*// 
     ## Save updated content to 'hold space'. 
     h 
    } 

### Added to fix an error pointed out by potong in comments. 
### ======================================================= 
    ## If last line, print lines left in 'hold space'. 
    $ { 
     x 
     s/^\n// 
     p 
    } 
### ======================================================= 


    ## Read next line. 
    b 
} 

## If line matches the pattern... 
/pattern/ { 

    ## Remove all content of 'hold space'. It has the five previous 
    ## lines, which won't be printed. 
    x 
    s/^.*$// 
    x 

    ## Read next four lines and append them to 'pattern space'. 
    N ; N ; N ; N 

    ## Delete all. 
    s/^.*$// 
} 

Ejecutar como:

sed -nf script.sed infile 
+0

Gracias, exactamente lo que estaba buscando (tengo las ocurrencias del patrón separadas por al menos 20 líneas). –

1

La idea es leer 5 líneas sin imprimirlas. Si encuentra el patrón, elimine las líneas no impresas y las 4 líneas abajo. Si no encuentra el patrón, recuerde la línea actual e imprima la primera línea no impresa. Al final, imprima lo que no está impreso.

sed -n -e '/XXXX/,+4{x;s/.*//;x;d}' -e '1,5H' -e '6,${H;g;s/\n//;P;s/[^\n]*//;h}' -e '${g;s/\n//;p;d}' fv.out 

Por supuesto, esto solo funciona si tiene una ocurrencia de su patrón en el archivo. Si tiene muchas, necesita leer 5 líneas nuevas después de encontrar su patrón, y se vuelve complicado si vuelve a tener su patrón en esas líneas. En este caso, creo que sed no es la herramienta adecuada.

+0

Gracias. Funcionó por primera vez. Pero, tengo muchas ocurrencias del patrón. Puede ser que tendré que ponerlo en un bucle para que grep no muestre más ocurrencia del patrón. ¿Qué sugieres aparte de usar sed? –

+0

Estaba trabajando en la solución de awk a continuación. En mi opinión, es mucho mejor porque es más fácil de entender y mantener. – jfg956

1

Esto podría funcionar para usted :

sed 'H;$!d;g;s/\([^\n]*\n\)\{5\}[^\n]*PATTERN\([^\n]*\n\)\{5\}//g;s/.//' file 

o esto:

awk --posix -vORS='' -vRS='([^\n]*\n){5}[^\n]*PATTERN([^\n]*\n){5}' 1 file 

una solución sed más eficiente:

sed ':a;/PATTERN/,+4d;/\([^\n]*\n\)\{5\}/{P;D};$q;N;ba' file 
+0

Gracias. Funciona perfectamente Pero, tengo algunos archivos enormes y toma mucho tiempo. –

+0

@PopulationXplosive He agregado una solución awk. Puede ser más rápido. – potong

+0

Gracias. La solución awk también ocupa mucho tiempo. Pero la nueva solución sed es bastante rápida. Un buen trazador de líneas de hecho. –

2

una solución utilizando awk:

awk '$0 ~ "XXXX" { lines2del = 5; nlines = 0; } 
    nlines == 5 { print lines[NR%5]; nlines-- } 
    lines2del == 0 { lines[NR%5] = $0; nlines++ } 
    lines2del > 0 { lines2del-- } 
    END { while (nlines-- > 0) { print lines[(NR - nlines) % 5] } }' fv.out 

Actualización:

Este es el sc ript explicó:

  • Recuerdo la última 5 líneas de la matriz lines mediante índices rotatorios (NR% 5; NR es el número de registro; en este caso líneas).
  • Si encuentro el patrón en la línea actual ($0 ~ "XXXX; $0 siendo el registro actual: en este caso, una línea, y ~ ser el extendido de expresiones regulares operador de partido), que restablezca el número de líneas de lectura y nota que Tengo 5 líneas para eliminar (incluida la línea actual).
  • Si ya leí 5 líneas, imprimo la línea actual.
  • Si no tengo líneas para eliminar (lo cual también es cierto si he leído 5 líneas, pongo la línea actual en el búfer e incremento el número de líneas. Observe cómo el número de líneas se reduce y luego se incrementa si se imprime una línea.
  • Si es necesario eliminar líneas, no imprimo nada y disminuyo el número de líneas para eliminar.
  • Al final del guión, imprimo todas las líneas que están en la matriz.

Mi versión original del guión fue el siguiente, pero terminó la optimización a la versión anterior:

awk '$0 ~ "XXXX" { lines2del = 5; nlines = 0; } 
    lines2del == 0 && nlines == 5 { print lines[NR%5]; lines[NR%5] } 
    lines2del == 0 && nlines < 5 { lines[NR%5] = $0; nlines++ } 
    lines2del > 0 { lines2del-- } 
    END { while (nlines-- > 0) { print lines[(NR - nlines) % 5] } }' fv.out 

awk es una gran herramienta! Recomiendo encarecidamente que encuentres un tutorial en la red y lo leas. Una cosa importante: awk funciona con Expresiones regulares extendidas (ERE). Su sintaxis es un poco diferente de Expresión regular estándar (RE) utilizada en sed, pero todo lo que se puede hacer con RE se puede hacer con ERE.

+0

Gracias. Está funcionando. Pero, soy un principiante absoluto para awk. Entonces, ¿puedes explicar el guion? Además, ¿cómo arreglas n1 y n2 por separado? Me parece que aquí has ​​tomado n1 = n2. –

+0

No entiendo su pregunta n1/n2. Explicar el guión es muy directo, estoy actualizando la publicación. – jfg956

+0

Gracias. Me preguntaba cómo se eliminan 4 líneas debajo (n2) del patrón. Como no tengo experiencia en awk, estaba pensando que coloque el número de líneas después de (n2) y antes (n1) el patrón como el mismo. Estaba muy confundido. Creo que sería genial si aprendiera awk también. –

1

Si usted está dispuesto a enviar el resultado a un archivo en lugar de la salida estándar, vim lo puede hacer de forma eficiente:

vim -c 'g/pattern/-5,+4d' -c 'w! outfile|q!' infile 

o

vim -c 'g/pattern/-5,+4d' -c 'x' infile 

para editar el archivo en el lugar.

Cuestiones relacionadas