A primeira seção descreve como usar sed
para alterar as primeiras ocorrências k em uma linha. A segunda seção estende essa abordagem para alterar apenas as primeiras ocorrências k em um arquivo, independentemente de em qual linha elas aparecem.
Solução orientada por linha
Com sed padrão, existe um comando para substituir a k-ésima ocorrência de uma palavra em uma linha. Se k
for 3, por exemplo:
sed 's/old/new/3'
Ou pode substituir todas as ocorrências por:
sed 's/old/new/g'
Nenhum destes é o que você quer.
O GNU sed
oferece uma extensão que mudará a k-ésima ocorrência e depois disso. Se k é 3, por exemplo:
sed 's/old/new/g3'
Estes podem ser combinados para fazer o que você quiser. Para alterar as 3 primeiras ocorrências:
$ echo old old old old old | sed -E 's/\<old\>/\n/g4; s/\<old\>/new/g; s/\n/old/g'
new new new old old
onde \n
é útil aqui porque podemos ter certeza de que nunca ocorre em uma linha.
Explicação:
Usamos três comandos de substituição sed
:
-
s/\<old\>/\n/g4
Esta é a extensão GNU para substituir a quarta e todas as ocorrências subseqüentes de
old
com\n
.O recurso de regex estendido
\<
é usado para corresponder ao início de uma palavra e\>
para corresponder ao final de uma palavra. Isso garante que apenas palavras completas sejam correspondidas. Regex estendido requer a opção-E
parased
. -
s/\<old\>/new/g
Somente as três primeiras ocorrências de
old
permanecem e isso substitui todas elas pornew
. -
s/\n/old/g
A quarta e todas as ocorrências restantes de
old
foram substituídas por\n
na primeira etapa. Isso os retorna de volta ao seu estado original.
Solução não-GNU
Se o GNU sed não estiver disponível e você quiser alterar as 3 primeiras ocorrências de old
para new
, use três comandos s
:
$ echo old old old old old | sed -E -e 's/\<old\>/new/' -e 's/\<old\>/new/' -e 's/\<old\>/new/'
new new new old old
Isso funciona bem quando k
é um número pequeno, mas não é muito adequado para k
.
Como alguns seds não-GNU não suportam a combinação de comandos com ponto e vírgula, cada comando aqui é introduzido com sua própria opção -e
. Também pode ser necessário verificar se o sed
suporta os símbolos de limite de palavras, \<
e \>
.
Solução orientada a arquivos
Podemos dizer ao sed para ler todo o arquivo e depois realizar as substituições. Por exemplo, para substituir as três primeiras ocorrências de old
usando um sed do tipo BSD:
sed -E -e 'H;1h;$!d;x' -e 's/\<old\>/new/' -e 's/\<old\>/new/' -e 's/\<old\>/new/'
Os comandos sed H;1h;$!d;x
leem todo o arquivo.
Como o acima não usa nenhuma extensão GNU, ele deve funcionar no BSD (OSX) sed. Note, pensou, que esta abordagem requer um sed
que pode lidar com linhas longas. O% GNUsed
deve estar bem. Aqueles que usam uma versão não-GNU de sed
devem testar sua capacidade de lidar com linhas longas.
Com um GNU sed, podemos usar o truque g
descrito acima, mas com \n
substituído por \x00
, para substituir as três primeiras ocorrências:
sed -E -e 'H;1h;$!d;x; s/\<old\>/\x00/g4; s/\<old\>/new/g; s/\x00/old/g'
Essa abordagem é dimensionada bem quando k
se torna grande. Isso pressupõe, no entanto, que \x00
não esteja em sua string original. Como é impossível colocar o caractere \x00
em uma string bash, isso geralmente é uma suposição segura.