Reemplace la enésima línea del patrón coincidente

Reemplace la enésima línea del patrón coincidente

Tengo el siguiente archivo de texto.

banana
apple
juice
mango
something

Estoy buscando patrón juicey quiero encontrar la segunda línea de ese patrón coincidente en orden inverso (es decir, 2 líneas encima del patrón coincidente) y reemplazarla con coconut.

Rendimiento esperado:

coconut
apple
juice
mango
something

Intenté seguir, pero simplemente elimina las dos líneas anteriores y no la que estoy buscando exactamente.

tac foo.txt |sed '/juice/I,+2 d' |tac
mango
something

Creo que modificar el script anterior funcionaría, pero no estoy seguro.

Nota: La coincidencia no volverá a ocurrir y no es necesario que sea una coincidencia exacta (es decir, la coincidencia también se puede encontrar en una línea larga). La coincidencia debe distinguir entre mayúsculas y minúsculas.

Respuesta1

Si edestá bien, necesita editar un archivo, no una secuencia, y solo hay uno  juice:

$ more <<-EOF | ed -s ./tmp.txt
	/juice/
	-2
	c
	coconut
	.
	w
	q
EOF
$

Encuentra la línea, sube dos líneas, ccuelga, writo y  quit.


Una variación aún más compacta, sugerida por @d-ben-knoble en los comentarios:

$ printf '%s\n' '/^juice$/-2s/.*/coconut/' w q | ed -s ./tmp.txt

Respuesta2

Siguiendo su planteamiento,

tac file|sed '/juice/{n;n;s/.*/coconut/}'|tac
  • /juice/coincide con una línea con juice.
  • n;n;imprime la línea actual y la siguiente.
  • s/.*/coconut/hace la sustitución.

Aparentemente tienes GNU sed, por lo que también puedes usarlo -zpara guardar todo el archivo en la memoria y editar directamente la línea dos arriba de jugo,

sed -rz 's/[^\n]*(\n[^\n]*\n[^\n]*juice)/coconut\1/' file

[^\n]significa "no una nueva línea" y el paréntesis ()captura el grupo reproducido por la \1referencia inversa.

Respuesta3

$ tac file | awk 'c&&!(--c){$0="coconut"} /juice/{c=2} 1' | tac
coconut
apple
juice
mango
something

Respuesta4

Aquí hay otro awkenfoque, esta vez un método de doble paso:

awk 'NR==FNR&&/juice/{m=FNR} NR>FNR&&FNR==m-2{$0="coconut"} (NR>FNR)' file file
  • Especificamos el archivodos vecescomo argumentos de línea de comandos, para que se procese dos veces.
  • En la primera pasada (donde FNR, el contador de líneas por archivo, es igual a NR, el contador de líneas global), simplemente identificamos en qué línea juiceocurre el patrón de búsqueda y lo almacenamos en una variable m.
  • En la segunda pasada, configuramos el número de línea m-2para el texto de reemplazo coconut.
  • Como regla general, imprimimos las líneas que incluyen cualquier modificación, pero solo en la segunda pasada (donde la condición NR>FNRse evalúa como "verdadera").

Si tiene GNU awk(algunas otras awkimplementaciones también lo admiten), puede acelerar un poco el proceso abortando el primer paso tan pronto como se encuentre la coincidencia usando el nextfilecomando:

awk 'NR==FNR&&/juice/{m=FNR;nextfile} NR>FNR&&FNR==m-2{$0="coconut"} (NR>FNR)' file file

información relacionada