Substitua a enésima linha do padrão correspondente

Substitua a enésima linha do padrão correspondente

Eu tenho o seguinte arquivo de texto.

banana
apple
juice
mango
something

Estou procurando por pattern juicee quero encontrar a segunda linha desse padrão correspondente na ordem inversa (ou seja, 2 linhas acima do padrão correspondente) e substituí-la por coconut.

Resultado esperado:

coconut
apple
juice
mango
something

Tentei seguir, mas apenas exclui as duas linhas acima e não a exata que estou procurando.

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

Acho que ajustar o script acima resolveria o problema, mas não tenho certeza.

Observação: não haverá nenhuma recorrência da correspondência e não precisa ser uma correspondência exata (ou seja, a correspondência também pode ser encontrada em uma linha longa). A correspondência deve diferenciar maiúsculas de minúsculas.

Responder1

Se edestiver tudo bem, você precisa editar um arquivo, não um fluxo, e há apenas um  juice:

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

Encontre a linha, suba duas linhas, change, write e  quit.


Uma variação ainda mais compacta, sugerida por @d-ben-knoble nos comentários:

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

Responder2

Seguindo sua abordagem,

tac file|sed '/juice/{n;n;s/.*/coconut/}'|tac
  • /juice/corresponde a uma linha com juice.
  • n;n;imprime a linha atual e a próxima.
  • s/.*/coconut/faz a substituição.

Aparentemente você tem o GNU sed, então você também pode usar -zpara colocar o arquivo inteiro na memória e editar diretamente a linha dois acima do suco,

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

[^\n]significa "não é uma nova linha" e os parênteses ()capturam o grupo reproduzido pela \1referência anterior.

Responder3

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

Responder4

Aqui está outra awkabordagem, desta vez um método de passagem dupla:

awk 'NR==FNR&&/juice/{m=FNR} NR>FNR&&FNR==m-2{$0="coconut"} (NR>FNR)' file file
  • Nós especificamos o arquivoduas vezescomo argumentos de linha de comando, para que seja processado duas vezes.
  • Na primeira passagem (onde FNR, o contador de linhas por arquivo, é igual a NR, o contador de linhas global), simplesmente identificamos em qual linha juiceocorre o padrão de pesquisa e o armazenamos em uma variável m.
  • Na segunda passagem, definimos o número da linha m-2para o texto de substituição coconut.
  • Como regra geral, imprimimos as linhas incluindo quaisquer modificações, mas apenas na segunda passagem (onde a condição NR>FNRé avaliada como “true”).

Se você possui GNU awk(algumas outras awkimplementações também suportam isso), você pode acelerar um pouco o processo abortando a primeira passagem assim que a correspondência for encontrada usando o nextfilecomando:

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

informação relacionada