BSD sed: substitua apenas a enésima ocorrência de um padrão

4

Usando o BSD sed ;

Como posso realizar a seguinte substituição ?:

Antes:

hello hello hello
hello hello hello

Depois:

hello world hello
hello hello hello

Em outras palavras; Como posso substituir apenas o N th ocorrência de um padrão?
(Ou, nesse caso, 2 nd ocorrência de um padrão?)

    
por tjt263 10.01.2016 / 14:44

2 respostas

1

Com qualquer POSIX sed :

$ sed -e'/hello/{' -e:1 -e'$!N;s/hello/world/2;t2' -eb1 -e\} -e:2 -en\;b2 <file
hello world hello
hello hello hello
  • Após a primeira correspondência /hello/ , encontramos um loop.

  • Dentro do loop :1 , lemos cada linha N ext para o espaço padrão, fazendo o comando s ubstitute somente para 2 nd ocorrência. Nós t est se o sucesso da substituição ou não. Se sim, nos deparamos com o loop :2 , caso contrário, repetir o loop com b1 .

  • Dentro do loop :2 , apenas imprimimos linhas remanescentes até o final do arquivo.

Note que esta abordagem irá armazenar todas as coisas entre dois hello no espaço padrão. Será um problema com arquivos enormes, quando o primeiro e o segundo estiverem longe um do outro.

    
por 14.01.2016 / 05:20
0

Pode ser mais fácil se você usar dois sed s. De fato, muitas coisas são, e elas são frequentemente mais rápidas dessa forma, também, em sistemas multicore, pelo menos.

:    infile =;<<"" \
sed -e's/$/ /;s/hello/&\n\n/g' -e'# marks lines with " $" and splits matches' |
sed -e:n   -e's/ $//;t'  -eG   -e'# sets up a test label, branches for " $"'  \
    -e's/o\n\{20\}$/o world/'  -e'# stacks a byte per match, edits nth match' \
    -e'x;N;x;N;s/\n\n*//;tn'   -e'# completes the stacking; recycles to top'  \
>outfile
hello hello hello hello hello hello hello hello hello
hello hello hello hello hello hello hello hello hello
hello hello hello hello hello hello hello hello hello
hello hello hello hello hello hello hello hello hello
hello hello hello hello hello hello hello hello hello
hello hello hello hello hello hello hello hello hello
hello hello hello hello hello hello hello hello hello
hello hello hello hello hello hello hello hello hello
hello hello hello hello hello hello hello hello hello
hello hello world hello hello hello hello hello hello hello
hello hello hello hello hello hello hello hello hello
hello hello hello hello hello hello hello hello hello
hello hello hello hello hello hello hello hello hello
hello hello hello hello hello hello hello hello hello

(Com um BSD sed , você desejará uma nova linha literal no lugar do n para os \n escapes no campo de substituição à direita)

Geralmente, é mais fácil adaptar o fluxo do que adaptar o editor de fluxo. A seqüência acima faz exatamente isso: marca cada linha inteira na entrada com um espaço à direita, mas divide as linhas de saída para cada ocorrência de hello . O segundo sed , em seguida, precisa apenas procurar uma linha que não termine no espaço para saber que ela deve incrementar sua contagem de pilha e, em seguida, apenas para corresponder explicitamente a 20a.

Claro que não precisa ser tão rigoroso. Você pode eliminar o o antes de \n\{20\}$ e deixá-lo fora da substituição. Isso substituirá apenas de a 20ª partida até a última entrada. Ou então você poderia fazer \n\{20,25\} para lidar apenas com uma série de correspondências. Ou ainda: \n\{20,25\}\(\n\{15\}\)*$ para lidar com um intervalo de 20,25 e a cada 10,15 ocorrência depois disso.

Aqui está uma amostra de saída dada a mesma entrada para o último mencionado ...

hello hello hello hello hello hello hello hello hello
hello hello hello hello hello hello hello hello hello
hello hello world hello world hello world hello world hello world hello world hello hello
hello hello hello hello hello hello hello hello world hello world
hello world hello world hello world hello world hello hello hello hello hello
hello hello hello hello hello world hello world hello world hello world hello world
hello world hello hello hello hello hello hello hello hello
    
por 15.01.2016 / 00:21