¿Cómo buscar un archivo por grupo de líneas y luego eliminar esas líneas?

¿Cómo buscar un archivo por grupo de líneas y luego eliminar esas líneas?

Tengo un archivo de registro que es una lista de características repetidas. Por ejemplo:

## This is the pattern of lines
time
urgency
icon_path
summary
body
appname

## Below is what the log file would actually look like
12:30
critical

test notification
notification
notify-send
11:00
low

earlier notification
notification
notify-send
10:46
normal

hello
world
dunstify

Estoy tratando de encontrar una manera de buscar un bloque/grupo de líneas que coincidan con mis términos de búsqueda y luego eliminarlos en bash. Como puede ver en el ejemplo anterior, a veces las líneas están vacías y otras veces están llenas. La mejor "solución" que he encontrado hasta ahora es usar sed '/12:30/,+5 d'o un poco mejor sed '/12:30/,/notify-send/d'. El problema con ambos es que el primero eliminará todas las apariciones de la marca de tiempo, eliminando así más de una entrada de registro; El problema con el otro comando es que si hay dos o más entradas con la misma hora y nombre de aplicación, se eliminarán todas las entradas coincidentes.

Lo que he estado tratando de hacer funcionar y he estado fallando espectacularmente es hacer algo como: sed '/12:30\n^.*$\n^.*$\ntest notification\nnotification\nnotify-send/d' /tmp/notification_log. Tenga en cuenta que la segunda y tercera líneas pueden ser cualquier cosa (las líneas de urgencia e icon_path respectivamente), razón por la cual las usé ^.*$(para ser franco, ni siquiera estoy seguro de si esa es la expresión regular adecuada).

EDITAR: Al usar el comando fallido anterior, esperaría que el resultado fuera:

11:00
low

earlier notification
notification
notify-send
10:46
normal

hello
world
dunstify

Ese comando tenía la entrada de:

12:30
*anything*
*anything*
test notification
notification
notify-send

Respuesta1

En realidad, no es tan difícil, siempre que todos los grupos tengan M líneas de largo, M sea fijo, los grupos no se superpongan y no necesitemos buscar el comienzo de ningún grupo. En nuestro caso M es 6.

sedle permite comparar varias líneas, pero como normalmente procesa una línea a la vez, necesita agregar explícitamente líneas adicionales al espacio del patrón. Lo haces con N:

sed 'N;N;N;N;N; /12:30\n.*\n.*\ntest notification\nnotification\nnotify-send/d'

El resto es tu código sin ^anclajes $. Las anclas suelen asociarse con "el principio de la línea" y "el final de la línea" respectivamente; pero en sedrealidad son "... de la cuerda". Cuando sedse procesa una línea a la vez no hay diferencia. En nuestro caso definitivamente debemos recordar que las anclas son "... de la cuerda". Ponerlos en el medio no tiene sentido. No es que nunca igualarían nada. sedEn primer lugar, no los interpretaría como anclas, sino que los interpretaría como literales ^y $.

No hay necesidad de anclajes "... de línea" en el medio de una cuerda. Cualquier línea excepto la última termina justo antes de algún carácter de nueva línea; cualquier línea, excepto la primera, comienza justo después de algún carácter de nueva línea. Entonces es suficiente para igualar \n.

Tal vez intentaste usar anclajes para asegurarte de que .*(que es codicioso y puede coincidir con caracteres de nueva línea) no coincida con más de una línea. Incluso si ^actuaran $como anclas "... de la línea", .*seguirían siendo codiciosos. Considere esto: el espacio del patrón sednunca contiene un carácter de nueva línea después de la última línea*. En nuestro caso sabemos que hay como máximo seis líneas en el espacio del patrón; y usamos \nexactamente cinco veces. Esto garantiza que cada fragmento de la expresión regular solo pueda coincidir con cierta línea en un grupo.

Aún así, las anclas pueden ayudar. El comando anterior puede eliminar un clúster que termina en notify-send-whatever. $es la manera correcta de prevenir esto. No hay más tiempo que 12:30ese partidos 12:30; pero es diferente para 2:30, por lo que en general ^también puede ser útil. El comando mejorado:

sed 'N;N;N;N;N; /^12:30\n.*\n.*\ntest notification\nnotification\nnotify-send$/d'

* Esto no significa que nunca pueda haber un carácter de nueva línea al final del espacio del patrón. Un carácter de nueva línea al final indica que hay una línea justo después del carácter. Es la última línea y está vacía. Y no hay ningún carácter de nueva línea después, por lo que "nunca hay un carácter de nueva línea después de la última línea".

información relacionada