Modificando o arquivo existente diretamente para substituir “foo” por “bar” APENAS para linhas que contenham “baz”

3

Eu tenho um arquivo food.txt da seguinte forma: -

mangoes|foo|faa  
oranges|foo|faa  
chocolates|foo|baz  

Estou tentando substituir foo por bar se a condição baz for atendida. (gostaria de usar uma expressão regular aqui b * z)
Atualmente usando o comando sed abaixo, mas isso não modifica diretamente o arquivo existente. Eu também não consigo usar uma expressão regular (b * z).

sed '/baz/s/foo/bar/g' food.txt

Por favor, sugira uma maneira diferente de "sed" para modificar o arquivo existente diretamente.

PS: tentei sed -i , mas gostaria de usar outro comando que não o sed.
Estou usando o mobaxterm (OS - Windows)

    
por jasmin 15.08.2016 / 12:22

4 respostas

2

A melhor ferramenta para edições automatizadas de texto é ex .

Embora seja possível chamar ex diretamente, encontrei no MobaXterm que tenho que chamar vim -e .

Assim, a melhor maneira de fazer essa edição automatizada no MobaXterm (e que também funcionará em outros sistemas * nix) é:

printf '%s\n' 'g/b.*z/s/foo/bar/g' x | vim -es food.txt

Para ser totalmente compatível com POSIX, só é necessário alterá-lo para:

printf '%s\n' 'g/b.*z/s/foo/bar/g' x | ex -s food.txt

No entanto, chamar o comando ex pode não funcionar corretamente no MobaXterm (ele não funciona na minha instalação). Tente a versão vim -es do comando se a versão ex falhar.

    
por 15.08.2016 / 22:10
7

Não vejo nada de errado em usar sed neste caso. É a ferramenta certa para o trabalho.

Seu comando funciona bem (nos dados fornecidos):

$ sed '/baz/s/foo/bar/g' food.txt
mangoes|foo|faa
oranges|foo|faa
chocolates|bar|baz

Usando uma expressão regular para corresponder a qualquer string que comece com |b e termine com z no final da linha (em vez de baz em qualquer lugar na linha):

$ sed '/|b.*z$/s/foo/bar/g' food.txt
mangoes|foo|faa
oranges|foo|faa
chocolates|bar|baz

Para fazer a alteração no arquivo (com GNU sed ):

$ sed -i '/|b.*z$/s/foo/bar/g' food.txt

ou (com qualquer implementação sed ):

$ sed '/|b.*z$/s/foo/bar/g' food.txt >tmpfile && mv tmpfile food.txt
    
por 15.08.2016 / 12:54
4

Usando awk com gsub() :

awk '/baz$/ {gsub("foo", "bar")};1' food.txt
  • Use qualquer padrão Regex para corresponder em vez de /baz$/ , se você quiser

  • se o padrão corresponder, faça gsub() para substituir as strings desejadas

Para edição interna, a versão recente do GNU awk (> = 4.1.0) tem a opção de modificação no local:

awk -i inplace '/baz$/ {gsub("foo", "bar")};1' food.txt

Caso contrário, você pode usar sponge do GNU moreutils ou usar um arquivo temporário:

awk '/baz$/ {gsub("foo", "bar")};1' food.txt >temp_food.txt && \
      mv temp_food.txt food.txt

Exemplo:

$ cat file.txt
mangoes|foo|faa
oranges|foo|faa
chocolates|foo|baz

$ awk '/baz$/ {gsub("foo", "bar")};1' file.txt
mangoes|foo|faa
oranges|foo|faa
chocolates|bar|baz
    
por 15.08.2016 / 12:35
3

Embora eu não tenha ideia de por que você não quer usar sed -i , que faz exatamente o que você precisa, outra opção seria perl :

$ perl -pe 's/foo/bar/g if /b*z/' food.txt 
mangoes|foo|faa  
oranges|foo|faa  
chocolates|bar|baz

O -pe significa "imprimir todas as linhas do arquivo de entrada depois de aplicar o script a ele.

E você pode usar -i para editar o arquivo:

perl -i -pe 's/foo/bar/g if /b*z/' food.txt 

Além disso, observe que a expressão regular b*z significa "corresponde a 0 ou mais b seguido por z . Ele funcionará aqui porque b*z corresponde a bar ignorando b e a e apenas correspondendo z . Em outras palavras, ele corresponderá a qualquer z , pois qualquer z será um exemplo de 0 b seguido por z . Acho que o que você provavelmente pretende usar é b. * z (ab seguido por 0 ou mais caracteres e depois az):

perl -i -pe 's/foo/bar/g if /b.*z/' food.txt 
    
por 15.08.2016 / 13:02