sed o awk: elimina n líneas siguiendo un patrón

105

¿Cómo mezclaría patrones y rangos numéricos en sed (o cualquier herramienta similar, awk por ejemplo)? Lo que quiero hacer es hacer coincidir ciertas líneas en un archivo y eliminar las siguientes n líneas antes de continuar, y quiero hacerlo como parte de una canalización.

Martín DeMello
fuente

Respuestas:

187

Voy a intentar esto.

Para eliminar 5 líneas después de un patrón (incluida la línea con el patrón):

sed -e '/pattern/,+5d' file.txt

Para eliminar 5 líneas después de un patrón (excluyendo la línea con el patrón):

sed -e '/pattern/{n;N;N;N;N;d}' file.txt
dogbane
fuente
14
Tenga en cuenta que el +Npatrón es una extensión GNU. Cambie el primero na an Nen su segundo ejemplo para que incluya la línea con el patrón.
Pausado hasta nuevo aviso.
2
¿Cómo eliminar todas las líneas después de que el patrón coincida? Estoy usando sed -e '/ <! - # content end -> </div> /, $ d' out.txt pero da un error que dice: sed: -e expresión # 1, char 24: caracteres adicionales después comando Gracias de antemano.
N mol
8
Lo que está sucediendo es similar pero ligeramente diferente en cada caso. En la primera receta, /pattern/,+5define un rango, que comienza con una línea que contiene "patrón" ( /pattern/) y termina 5 líneas después ( +5). El último carácter des un comando que se ejecuta en cada línea de ese rango, que es "eliminar". En la segunda receta, en lugar de hacer coincidir un rango, coincide solo en la línea que contiene el patrón ( /pattern/) y luego ejecuta una serie de comandos:, {n;N;N;N;N;d}que básicamente imprime la siguiente línea ( n) y luego lee y finalmente descarta las siguientes 4 líneas ( N;N;N;N;d).
pimlottc
18
En sistemas Mac / OS X, debe agregar un punto y coma antes del corchete de cierre:sed -e '/pattern/{n;N;N;N;N;d;}' file.txt
AvL
1
Para completar: Para eliminar todas las líneas que siguen un patrón determinado, something haga:, sed -E '/^something$/,$d'donde -Ees la expresión regular extendida de portabilidad POSIX.
not2qubit
7

Sin extensiones GNU (por ejemplo, en macOS):

Para eliminar 5 líneas después de un patrón (incluida la línea con el patrón)

 sed -e '/pattern/{N;N;N;N;d;}'

Agregar -i ''para editar en el lugar.

thakis
fuente
6

awkSoluciones sencillas :

Suponga que la expresión regular que se utilizará para encontrar líneas coincidentes se almacena en la variable de shell $regexy el recuento de líneas para saltar $count.

Si también se debe omitir la línea coincidente ( $count + 1se omiten las líneas):

... | awk -v regex="$regex" -v count="$count" \
  '$0 ~ regex { skip=count; next } --skip >= 0 { next } 1'

Si la línea coincidente no debe omitirse (se omiten las$count líneas posteriores a la coincidencia):

... | awk -v regex="$regex" -v count="$count" \
  '$0 ~ regex { skip=count; print; next } --skip >= 0 { next } 1'

Explicación:

  • -v regex="$regex" -v count="$count"define awkvariables basadas en variables de shell del mismo nombre.
  • $0 ~ regex coincide con la línea de interés
    • { skip=count; next }inicializa el recuento de saltos y pasa a la siguiente línea, saltando efectivamente la línea correspondiente; en la segunda solución, los printantes nextasegura que es no saltarse.
    • --skip >= 0 disminuye el número de saltos y actúa si es (todavía)> = 0, lo que implica que la línea en cuestión debe omitirse.
    • { next } pasa a la siguiente línea, saltándose efectivamente la línea actual
  • 1es una abreviatura de uso común para { print }; es decir, la línea actual simplemente se imprime
    • Solo las líneas que no coinciden ni se omiten llegan a este comando.
    • La razón por la que 1es equivalente a { print }es que 1se interpreta como un patrón booleano que por definición siempre se evalúa como verdadero, lo que significa que su acción asociada (bloque) se ejecuta incondicionalmente. Dado que no hay ninguna acción asociada en este caso, el valor awkpredeterminado es imprimir la línea.
mklement0
fuente
3

Esto podría funcionar para ti:

cat <<! >pattern_number.txt
> 5 3
> 10 1
> 15 5
> !
sed 's|\(\S*\) \(\S*\)|/\1/,+\2{//!d}|' pattern_number.txt |
sed -f - <(seq 21)
1 
2
3
4
5
9
10
12
13
14
15
21
potong
fuente
10
Vaya, eso es críptico.
pimlottc
3
Una solución inteligente (aunque específica de GNU-Sed), pero pocas personas se beneficiarán de ella, a menos que agregue una explicación. pattern_number.txtes un archivo de 2 columnas que contiene el patrón que debe coincidir en la 1ª columna y en la 2ª el número de líneas que se deben omitir. El primer sedcomando transforma el archivo en un sedscript que realiza la correspondiente coincidencia y omisión; ese script se proporciona a través de -fy stdin ( -) al segundo sedcomando. El segundo sedcomando opera en un archivo de entrada ad-hoc de muestra formado a partir de la salida de seq 21para demostrar que funciona.
mklement0
Además, la solución viene con una advertencia: el método que utiliza para no omitir la primera línea (la que coincide con el patrón) tiene el efecto secundario de no omitir líneas duplicadas en el rango.
mklement0
Ese es un uso impresionante de sed.
Travis Rodman
3

Usando Perl

$ cat delete_5lines.txt
1
2
3
4
5 hello
6
7
8
9
10
11 hai
$ perl -ne ' BEGIN{$y=1} $y=$.  if /hello/ ; print if $y==1 or $.-$y > 5 ' delete_5lines.txt
1
2
3
4
11 hai
$
stack0114106
fuente
2

Esta solución le permite pasar "n" como parámetro y leerá sus patrones desde un archivo:

awk -v n=5 '
    NR == FNR {pattern[$0]; next}
    {
        for (patt in pattern) {
            if ($0 ~ patt) {
                print # remove if you want to exclude a matched line
                for (i=0; i<n; i++) getline
                next
            }
        }
        print
    }
' file.with.patterns -

El archivo llamado "-" significa stdin para awk, por lo que es adecuado para su canalización

Glenn Jackman
fuente
2
¡awk es capaz de ser mucho más parecido a Perl de lo que pensaba!
Martin DeMello