Quero substituir apenas as primeiras k
instâncias de uma palavra.
Como posso fazer isso?
Por exemplo. O arquivo Say foo.txt
contém 100 ocorrências de instâncias da palavra 'linux'.
Preciso substituir apenas as 50 primeiras ocorrências.
text-processing
sed
awk
narendra-choudhary
fonte
fonte
Respostas:
A primeira seção a seguir descreve o uso
sed
para alterar as primeiras k ocorrências em uma linha. A segunda seção estende essa abordagem para alterar apenas as primeiras k ocorrências em um arquivo, independentemente da linha em que elas aparecem.Solução orientada a linhas
Com o sed padrão, existe um comando para substituir a ocorrência de k-ésima de uma palavra em uma linha. Se
k
for 3, por exemplo:Ou, pode-se substituir todas as ocorrências por:
Nenhuma delas é o que você deseja.
O GNU
sed
oferece uma extensão que mudará a ocorrência de k-és e depois disso. Se k for 3, por exemplo:Estes podem ser combinados para fazer o que você deseja. Para alterar as 3 primeiras ocorrências:
onde
\n
é útil aqui, porque podemos ter certeza de que nunca ocorre em uma linha.Explicação:
Usamos três
sed
comandos de substituição:s/\<old\>/\n/g4
Essa é a extensão GNU para substituir a quarta e todas as ocorrências subsequentes de
old
com\n
.O recurso regex estendido
\<
é usado para corresponder ao início de uma palavra e\>
ao final de uma palavra. Isso garante que apenas as palavras completas sejam correspondidas. Regex estendida requer a-E
opção desed
.s/\<old\>/new/g
Apenas as três primeiras ocorrências de
old
permanecem e isso as substitui por todasnew
.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 ao seu estado original.Solução não GNU
Se o GNU sed não estiver disponível e você desejar alterar as 3 primeiras ocorrências de
old
paranew
, use trêss
comandos:Isso funciona bem quando
k
é um número pequeno, mas varia de mal a grandek
.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
-e
opção. Também pode ser necessário verificar se vocêsed
suporta os símbolos de limite de palavras\<
e\>
.Solução orientada a arquivos
Podemos dizer ao sed para ler o arquivo inteiro e depois executar as substituições. Por exemplo, para substituir as três primeiras ocorrências do
old
uso de um sed no estilo BSD:Os comandos sed
H;1h;$!d;x
lêem o arquivo inteiro.Como o descrito acima não usa nenhuma extensão GNU, ele deve funcionar no BSD (OSX) sed. Observe, pensou, que essa abordagem requer um
sed
que possa lidar com linhas longas. GNUsed
deve estar bem. Aqueles que usam uma versão não-GNUsed
devem testar sua capacidade de lidar com longas filas.Com um GNU sed, podemos usar ainda mais o
g
truque descrito acima, mas com\n
substituído por\x00
, para substituir as três primeiras ocorrências:Essa abordagem escala bem e
k
se torna grande. Isso pressupõe, porém, que\x00
não esteja na sua string original. Como é impossível colocar o caractere\x00
em uma string do bash, isso geralmente é uma suposição segura.fonte
tr '\n' '|' < input_file | sed …
. Mas, é claro, isso converte toda a entrada em uma linha, e alguns seds não-GNU não podem lidar com linhas arbitrariamente longas. (2) Você diz: “… acima, a cadeia de caracteres citada'|'
deve ser substituída por qualquer caractere, ou cadeia de caracteres,…” Mas você não pode usartr
para substituir um caractere por uma cadeia de caracteres (de comprimento> 1). (3) No seu último exemplo, você diz-e 's/\<old\>/new/' -e 's/\<old\>/w/' | tr '\000' '\n'\>/new
. Este parece ser um erro de digitação-e 's/\<old\>/new/' -e 's/\<old\>/new/' -e 's/\<old\>/new/' | tr '\000' '\n'
.Usando o Awk
Os comandos awk podem ser usados para substituir as primeiras N ocorrências da palavra pela substituição.
Os comandos serão substituídos apenas se a palavra for uma correspondência completa.
Nos exemplos abaixo, estou substituindo as primeiras
27
ocorrências deold
pornew
Usando sub
Substituindo o Campo Manualmente
Executando uma verificação antes
RESULTADOS
Por exemplo
para
fonte
$i
pouco, tem sido editado, graças :)Digamos que você queira substituir apenas as três primeiras instâncias de uma string ...
nota: o acima provavelmente não funcionará com comentários incorporados
... ou, no meu exemplo, de um '1' ...
SAÍDA:
Lá eu uso duas técnicas notáveis. Em primeiro lugar, toda ocorrência de
1
em uma linha é substituída por\n1
. Dessa forma, ao fazer as substituições recursivas a seguir, posso ter certeza de não substituir a ocorrência duas vezes se minha cadeia de substituição contiver minha cadeia de substituição. Por exemplo, se eu substituirhe
porhey
ele ainda funcionará.Eu faço assim:
Em segundo lugar, estou contando as substituições adicionando um caractere ao
h
espaço antigo para cada ocorrência. Quando chego a três, não ocorre mais. Se você aplicar isso aos seus dados e alterar as\{3\}
substituições totais desejadas e os/\n1/
endereços para o que você deseja substituir, substitua apenas o número que desejar.Eu só fiz todas as
-e
coisas para facilitar a leitura. POSIXly Poderia ser escrito assim:E com GNU
sed
:Lembre-se também de que
sed
é orientado a linhas - ele não lê o arquivo inteiro e tenta repetir o processo, como costuma acontecer em outros editores.sed
é simples e eficiente. Dito isto, muitas vezes é conveniente fazer algo como o seguinte:Aqui está uma pequena função shell que agrupa em um comando simplesmente executado:
Então, com isso eu posso fazer:
...e pegue...
...ou...
...para obter...
... ou, para corresponder ao seu exemplo (em uma ordem de magnitude menor) :
fonte
Uma alternativa curta no Perl:
Altere o valor de `$ n $ ao seu gosto.
Como funciona:
new
porold
(s/old/new/
) e sempre que pode, ele incrementa a variável$i
(++$i
).1 while ...
) desde que tenha feito menos do que$n
substituições no total e possa fazer pelo menos uma substituição nessa linha.fonte
Use um loop de shell e
ex
!Sim, é um pouco pateta.
;)
Nota: Isso pode falhar se houver menos de 50 instâncias
old
no arquivo. (Não testei.) Nesse caso, deixaria o arquivo inalterado.Melhor ainda, use o Vim.
Explicação:
fonte
Uma solução simples, mas não muito rápida, é executar um loop sobre os comandos descritos em /programming/148451/how-to-use-sed-to-replace-only-the-first-occurrence-in-a -Arquivo
Esse comando sed em particular provavelmente funciona apenas para o GNU sed e se newword não faz parte do oldword . Para sed não GNU, veja aqui como substituir apenas o primeiro padrão em um arquivo.
fonte
Com o GNU,
awk
você pode definir o separador de registrosRS
como a palavra a ser substituída, delimitada pelos limites da palavra. É o caso de definir o separador de registros na saída como a palavra de substituição para os primeirosk
registros, mantendo o separador de registros original pelo restanteOU
fonte