Grep para encontrar a linha correta, sed para alterar o conteúdo e colocá-lo novamente no arquivo original?

9

Estou tentando alterar uma única palavra em uma linha específica de um arquivo, mas estou tendo problemas para conectar todos juntos.

Basicamente, em uma linha do meu arquivo existe a palavra-chave 'firmware_revision' e nesta linha (e somente nesta linha) quero substituir a palavra 'teste' pela palavra 'produção'.

Então eu posso fazer isso:

grep 'firmware_revision' myfile.py | sed 's/test/production'

Isso selecionará a linha que eu quero e executarei a substituição, mas não consigo descobrir como colocar essa nova linha no arquivo original para substituir a linha antiga. Obviamente, não posso apenas redirecioná-lo de volta para o arquivo, então o que devo fazer?

Mesmo se eu usar temporários, usando greppara obter apenas a linha necessária, perco todos os outros dados no arquivo, para que eu não possa mais apenas redirecioná-los para um arquivo temporário e substituir o original pela temp.

Editar - Alguém pediu mais informações

Digamos que eu tenho um arquivo cheio de linhas como esta

[
  ('key_name1', str, 'value1', 'Description'),
  ('key_name2', str, 'value2', 'Description'),
  ('key_name3', str, 'value3', 'Description'),
  ('firmware_revision', str, 'my-firmware-name-test', 'Firmware revision name')
]

Agora, quero escrever um script (de preferência uma linha) que encontre a linha que contém 'firmware_revision' e altere todas as instâncias da palavra 'test' nessa linha para 'produção'. A palavra 'teste' pode estar em outros lugares nesse arquivo e eu não quero que elas sejam alteradas. Portanto, para deixar claro, quero alterar a linha acima para

('firmware_revision', str, 'my-firmware-name-production', 'Firmware revision name')

Como eu faço isso?

John Allard
fonte
5
sedé muito poderoso, ele pode executar ambas as funções ( grepe substituição), mas precisaremos de mais informações sobre a aparência da linha para ajudá-lo.
grochmal
Vou acrescentar mais informações para o post original
John Allard
7
Tente:sed -i.bak '/firmware_revision/ s/test/production/' myfile.py
John1024
1
Ah, funcionou perfeitamente! Você pode explicar a sintaxe?
31516 John Allard
@JohnAllard Muito bom. Eu adicionei uma resposta com explicação.
John1024

Respostas:

22

Tentar:

sed -i.bak '/firmware_revision/ s/test/production/' myfile.py

Aqui, /firmware_revision/atua como uma condição. É verdade para linhas que correspondem à regex firmware_revisione false para outras linhas. Se a condição for verdadeira, o comando a seguir é executado. Nesse caso, esse comando é um comando substituto que substitui a primeira ocorrência de testwith production.

Em outras palavras, o comando s/test/production/é executado apenas nas linhas que correspondem ao regex firmware_revision. Todas as outras linhas passam inalteradas.

Por padrão, o sed envia sua saída para a saída padrão. Você, no entanto, queria alterar o arquivo no local. Então, nós adicionamos a -iopção. Em particular, -i.bakfaz com que o arquivo seja alterado no lugar com uma cópia de backup salva com uma .bakextensão.

Se você decidiu que o comando funciona para você e deseja viver perigosamente e não criar um backup, com o GNU sed (Linux), use:

sed -i '/firmware_revision/ s/test/production/' myfile.py

Por outro lado, no BSD (OSX), a -iopção deve ter um argumento. Se você não deseja manter um backup, forneça um argumento vazio. Assim, use:

sed -i '' '/firmware_revision/ s/test/production/' myfile.py

Editar

Na edição da pergunta, o OP solicita que todas as ocorrências testna linha sejam substituídas por production. Nesse caso, adicionamos a gopção ao comando substitute para uma substituição global (para essa linha):

sed -i.bak '/firmware_revision/ s/test/production/g' myfile.py
John1024
fonte
5
Para completar, você também pode explicar a -i.bakparte.
Stephen Harris
5
@StephenHarris Good point. Explicação de -iadicionado.
John1024
Eu sinto que você provavelmente poderia alcançar as estrelas, de pé sobre o livro explicando todas as coisas que sedpode fazer ...
KlaymenDK
@ John1024 Para as pessoas que não são do BSD, você poderia adicionar os motivos do primeiro ''depois -i?
Hastur
3
O OP queria alterar todas as instâncias da palavra 'teste' nessa linha para 'produção' . É importante acrescentar / g ao comando sed neste caso: caso sed -i '/firmware_revision/ s/test/production/g' myfile.pycontrário, apenas a primeira instância será alterada.
tipo protuberância
4

Em máquinas mais antigas com old-school sedque não oferece suporte à opção -i:

TF=$( mktemp -t "${0##*/}"_$$_XXXXXXXX ) && \
trap 'rm -f "$TF"' EXIT HUP INT QUIT TERM && \
sed '/firmware_revision/ s/test/production/' myfile.py >"$TF" && \
mv -f "$TF" myfile.py
Satō Katsura
fonte
1
Nessas máquinas mais antigas, você pode simplesmente usar ede fazê-lo em uma linha:printf %s\\n g/firmware_revision/s/test/production/g w q | ed -s myfile.py
don_crissti
1
@don_crissti É verdade que, mas ed(1)não oferece nenhum pretexto para mostrar o uso de mktemp(1).
Satō Katsura
Se você estiver usando um arquivo temporário, também poderá manter o inode e as permissões do arquivo original, da mesma forma que edfaz. Em vez de mv -f "$TF" myfile.pl, use cat "$TF" > myfile.pl && rm -f "$TF". BTW, é uma boa prática usar nomes de variáveis ​​em minúsculas (em $tfvez de $TF), eles garantem que não entrem em conflito com nenhum vars incorporado do bash (provavelmente outras conchas de bourne também).
cas
@cas Re:: cat "$TF" > myfile.pltente o seguinte: touch a b; chmod 0444 a; cat b >a(BTW, sed -itambém não funcionará nesse caso). Melhor deixar o usuário lidar com isso. Re:: rm -f "$TF"não é necessário, veja trap ... EXIT. Re: em $tfvez de $TF: talvez; é uma questão de estilo.
Satō Katsura
Esse é o comportamento normal e esperado das permissões de arquivo unix. Meu ponto foi que, ao criar um novo arquivo e movê-lo sobre o original, 1. resultará em um novo inode para esse nome de arquivo (quebrando os links físicos). 2. possivelmente altere as permissões, se a corrente umask diferir das permissões originais. Quanto aos Vars maiúsculos, não é apenas uma questão de estilo - muitas pessoas que cometeram o erro de usar PATH ou RANDOM ou SHELL maiúsculas ou o que quer que seja para suas próprias variáveis ​​descobriram da maneira mais difícil. (e sim, eu mesmo uso varios maiúsculos. Sei que está errado, estou tentando quebrar o hábito).
12286