Conheço os números de linha com antecedência e os mantenho em outro arquivo:
cat linenos
2
15
42
44
... etc
como você vê, as linhas não são adjacentes, então não posso usar um intervalo para sed
. O objetivo é modificar as linhas do arquivo de destino, digamos, prefixando-as com um marcador como MARKER
A maneira direta é chamar sed
várias vezes para modificar cada linha:
for l in $(cat linenos)
do
sed -i "${l}s/^/MARKER/" target_file
done
que aparentemente chamará sed várias vezes.
CUIDADO : *Esta abordagem não é apenas ineficiente, mas também pode fazer com que as coisas dêem errado se a modificação for diferente de inserir um marcador como este. Qualquer comando sed de exclusão ou inserção de linha, como dar, tornará o número de linha inicial em linenos inválido para as próximas execuções do sed no loop.
O que você sugeriria para melhorar/otimizar isso?
Exemplo de arquivo linenos
cat linenos
2
5
Exemplo de arquivo_destino
cat target_file
line one
line two
line three
line four
line five
line six
Resultado esperado de target_file modificado
cat target_file
line one
MARKERline two
line three
line four
MARKERline five
line six
A abordagem possível que eu criei é criar dinamicamente o cenário sed
SEDCMD=$(for l in $(cat linenos); do echo -n "${l}s/^/MARK/;" ; done)
sed -i -e "$SEDCMD" targetfile
A abordagem abaixo do @steeldriver compartilha a ideia, mas é mais elegante e concisa
Você pode usar o próprio sed (ou outro utilitário de processamento de texto de sua escolha) para transformar os números de linha em expressões sed e depois passá-los para o sed usando a
-f
opçãoEx.
Isso pelo menos só chama sed duas vezes .
Com
perl
(de onde o GNUsed
veio-i
):Alimentamos a lista de números de linha no
perl
stdin de . Isso é lido noBEGIN
bloco.Para cada linha de entrada, convertemos essa linha em um número com
0+$_
. Isso faz com que o caractere de nova linha desapareça e também canoniza os números (todos 1e0, 1, 01 se tornam 1).A
%l
tabela de hash é preenchida com valor1
para cada número de linha como a chave.O
target_file
é processado no-p
loop principal ondeMARKERS
é anexado às linhas em que o número da linha atual ($.
) é encontrado%l
com um valor diferente de zero.ou para economizar um pouco de memória:
Se você quiser edição "inplace" (o mesmo que perl e GNU sed com
-i
) use GNU awk e altereawk '...'
eawk -i inplace '...'
adicione umprint;
antes danext
instrução para que seulinenos
arquivo não seja esvaziado. IMHO é mais simples fazer isso com qualquer awk (ou qualquer outra ferramenta UNIX):Se
fileN
contém o número de linhas a serem modificadas, etarget_file
é o arquivo de texto que contém as linhas a serem modificadas. A solução mínima exigirá a leitura de cada arquivo uma vez.Classificado
Se o arquivo que contém os números das linhas contiver um número (maior que 1) por linha, estiver ordenado e não houver repetições, podemos usar:
O que manterá apenas uma linha na memória (de cada arquivo) e percorrerá os dois arquivos do início ao fim. No entanto, uma vez que o awk tenha processado uma linha, a linha 15, por exemplo, ele não voltará à linha 12, por exemplo. Portanto, o arquivo
lineN
deve ser classificado (não repetido e maior que 1) para que isso funcione.não triados
Claro, a solução ingênua é que o arquivo de números de linha pode ser classificado
sort -nu fileN
.Mas, se a lista de números de linha puder ser desordenada (e repetida), podemos usar sed ,
ed
(o precursor desed
), ou awk (mais tarde):Converta cada linha em
lineN
um comando de edição sed comos/^/MARKER /
. Ou shell printf ou sed poderia fazer isso:Observe que no último caso a edição é feita diretamente e no arquivo original. O último comando
w
grava as modificações no arquivo. Se for necessário imprimir o resultado, use a terceira opção, que imprimirá todas as linhas.awk
No awk, capture o todo
fileN
na memória e processetarget_file
Ou, com uma variável para controlar quando a lista de arquivos com números de linha terminou:
Observe que a última versão permite vários arquivos com números de linha, como
fileN
efileK
no exemplo.Observe também que as versões awk não processam números de linha repetidos. Todos os números de linha repetidos são processados apenas uma vez.
Outra maneira, usando
ed
em vez desed
para modificar otarget_file
local: