sed editar o arquivo no lugar

Eu estou tentando descobrir se é possível editar um arquivo em um único comando sed sem manualmente o streaming de conteúdo editado em um novo arquivo e, em seguida, renomear o novo arquivo para o nome do arquivo original. Eu tentei a opção -i mas meu sistema Solaris disse que -i é uma opção ilegal. Existe um caminho diferente?

A opção -i transmite o conteúdo editado para um novo arquivo e, em seguida, o renomeia nos bastidores.

Exemplo:

 sed -i 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' filename 

e

 sed -i '' 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' filename 

no macOS .

Em um sistema em que o sed não tem a capacidade de editar arquivos, acho que a melhor solução seria usar o perl :

 perl -pi -e 's/foo/bar/g' file.txt 

Embora isso crie um arquivo temporário, ele substitui o original porque um sufixo / extensão vazio no local foi fornecido.

Note que no OS X você pode receber erros estranhos como “código de comando inválido” ou outros erros estranhos ao executar este comando. Para corrigir esse problema, tente

 sed -i '' -e "s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g"  

Isso ocorre porque, na versão OSX do sed , a opção -i espera um argumento de extension portanto, seu comando é realmente analisado como o argumento de extension e o caminho do arquivo é interpretado como o código de comando. Fonte: https://stackoverflow.com/a/19457213

O seguinte funciona bem no meu mac

 sed -i.bak 's/foo/bar/g' sample 

Estamos substituindo foo por barra no arquivo de amostra. Backup do arquivo original será salvo em sample.bak

Para editar inline sem backup, use o seguinte comando

 sed -i'' 's/foo/bar/g' sample 

Uma coisa a notar, o sed não pode escrever arquivos por si só, já que o único propósito do sed é atuar como um editor no “stream” (isto é, pipelines de stdin, stdout, stderr e outros >&n buffers, sockets e similares) . Com isso em mente, você pode usar outro comando tee para gravar a saída de volta no arquivo. Outra opção é criar um patch de piping o conteúdo em diff .

Método Tee

sed '/regex/' | tee

Método de patch

sed '/regex/' | diff -p /dev/stdin | patch

ATUALIZAR:

Além disso, observe que o patch irá fazer o arquivo mudar da linha 1 da saída do diff:

O patch não precisa saber qual arquivo acessar, pois isso é encontrado na primeira linha da saída do diff:

 $ echo foobar | tee fubar $ sed 's/oo/u/' fubar | diff -p fubar /dev/stdin *** fubar 2014-03-15 18:06:09.000000000 -0500 --- /dev/stdin 2014-03-15 18:06:41.000000000 -0500 *************** *** 1 **** ! foobar --- 1 ---- ! fubar $ sed 's/oo/u/' fubar | diff -p fubar /dev/stdin | patch patching file fubar 

Não é possível fazer o que você quer com sed . Mesmo versões do sed que suportam a opção -i para editar um arquivo no local fazem exatamente o que você declarou explicitamente que não deseja: eles gravam em um arquivo temporário e renomeam o arquivo. Mas talvez você possa apenas usar o ed . Por exemplo, para alterar todas as ocorrências de foo para bar no arquivo file.txt , você pode fazer:

 echo ',s/foo/bar/g; w' | tr \; '\012' | ed -s file.txt 

A syntax é semelhante a sed , mas certamente não é exatamente a mesma.

Mas talvez você deva considerar por que não deseja usar um arquivo temporário renomeado. Mesmo que você não tenha um -i suporte ao sed , você pode facilmente escrever um script para fazer o trabalho para você. Em vez do sed -i 's/foo/bar/g' file , você poderia fazer o inline file sed 's/foo/bar/g' . Tal roteiro é trivial para escrever. Por exemplo:

 #!/bin/sh -e IN=$1 shift trap 'rm -f $tmp' 0 tmp=$( mktemp ) < $IN "$@" >$tmp && cat $tmp > $IN # preserve hard links 

deve ser adequado para a maioria dos usos.

sed suporta edição no local. De man sed :

 -i[SUFFIX], --in-place[=SUFFIX] edit files in place (makes backup if extension supplied) 

Exemplo :

Digamos que você tenha um arquivo hello.txt com o texto:

 hello world! 

Se você quiser manter um backup do arquivo antigo, use:

 sed -i.bak 's/hello/bonjour' hello.txt 

Você vai acabar com dois arquivos: hello.txt com o conteúdo:

 bonjour world! 

e hello.txt.bak com o conteúdo antigo.

Se você não quiser manter uma cópia, apenas não passe o parâmetro de extensão.

Você poderia usar o vi

 vi -c '%s/foo/bar/g' my.txt -c 'wq' 
 mv file.txt file.tmp && sed 's/foo/bar/g' < file.tmp > file.txt 

Deve preservar todos os hardlinks, já que a saída é direcionada de volta para sobrescrever o conteúdo do arquivo original e evita qualquer necessidade de uma versão especial do sed.

Se você estiver substituindo a mesma quantidade de caracteres e depois de ler cuidadosamente a edição “In-place” dos arquivos …

Você também pode usar o operador de redirecionamento <> para abrir o arquivo para ler e gravar:

 sed 's/foo/bar/g' file 1<> file 

Veja ao vivo:

 $ cat file hello i am here # see "here" $ sed 's/here/away/' file 1<> file # Run the `sed` command $ cat file hello i am away # this line is changed now 

From Bash Reference Manual → 3.6.10 Abrindo descritores de arquivo para leitura e escrita :

O operador de redirecionamento

 [n]<>word 

faz com que o arquivo cujo nome é a expansão da palavra seja aberto para leitura e gravação no descritor de arquivo n ou no descritor de arquivo 0, se n não for especificado. Se o arquivo não existir, ele será criado.

Você não especificou qual shell está usando, mas com o zsh você pode usar a construção =( ) para conseguir isso. Algo ao longo das linhas de:

 cp =(sed ... file; sync) file 

=( ) é semelhante a >( ) mas cria um arquivo temporário que é automaticamente excluído quando o cp termina.

Como Moneypenny disse em Skyfall: “Às vezes as formas antigas são melhores”. Kincade disse algo semelhante mais tarde.

$ printf ‘, s / false / true / g \ nw \ n’ | ed {YourFileHere}

Edição feliz no lugar. Adicionado ‘\ nw \ n’ para gravar o arquivo. Desculpas por solicitação de resposta atrasada.

Muito bons exemplos. Eu tive o desafio de editar muitos arquivos e a opção -i parece ser a única solução razoável usando-a dentro do comando find. Aqui o script para adicionar “versão:” na frente da primeira linha de cada arquivo:

 find . -name pkg.json -print -exec sed -i '.bak' '1 s/^/version /' {} \;