Como reportar alterações “sed” no local

20

Ao usar sed para substituir strings in-place, existe uma maneira de fazer com que ele relate as alterações que faz (sem depender de uma diferença entre arquivos antigos e novos)?

Por exemplo, como posso alterar a linha de comando

find . -type f | xargs sed -i 's/abc/def/g'

para que eu possa ver as alterações feitas na hora?

    
por ricab 23.10.2013 / 19:48

5 respostas

7

Você pode usar sed w sinalizador com /dev/stderr , /dev/tty , /dev/fd/2 , se suportado em seu sistema. Por exemplo. com uma entrada file como:

foo first
second: missing
third: foo
none here

executando

sed -i '/foo/{
s//bar/g
w /dev/stdout
}' file

saídas:

bar first
third: bar

embora file conteúdo tenha sido alterado para:

bar first
second: missing
third: bar
none here

Então, no seu caso, executando:

find . -type f -printf '\n%p:\n' -exec sed -i '/foo/{
s//bar/g
w /dev/fd/2
}' {} \;

edita os arquivos no local e a saída:


./file1:
bar stuff
more bar

./file2:

./file3:
bar first
third: bar

Você também pode imprimir algo como original line >>> modified line , por exemplo:

find . -type f -printf '\n%p:\n' -exec sed -i '/foo/{
h
s//bar/g
H
x
s/\n/ >>> /
w /dev/fd/2
x
}' {} \;

edita os arquivos no local e saídas:


./file1:
foo stuff >>> bar stuff
more foo >>> more bar

./file2:

./file3:
foo first >>> bar first
third: foo >>> third: bar
    
por 05.05.2015 / 16:23
15

Você pode fazer isso em duas etapas usando a ação p rint na primeira passagem:

find . -type f | xargs sed --quiet 's/abc/def/gp'

onde --quiet faz o sed não mostrar todas as linhas e o% sufixo p mostra apenas as linhas onde a substituição coincidiu.

Isso tem a limitação de que o sed não mostrará quais arquivos estão sendo alterados, o que, é claro, poderia ser corrigido com alguma complexidade adicional.

    
por 23.10.2013 / 20:05
8

Eu não acho que isso seja possível, mas uma solução alternativa é usar o perl:

find . -type f | xargs perl -i -ne 's/abc/def/ && print STDERR' 

Isso imprimirá as linhas alteradas como erro padrão. Por exemplo:

$ cat foo
fooabcbar
$ find . -type f | xargs perl -i -ne 's/abc/def/ && print STDERR' 
foodefbar

Você também pode tornar isso um pouco mais complexo, imprimindo o número da linha, o nome do arquivo, a linha original e a linha alterada:

$ find . -type f | 
   xargs perl -i -ne '$was=$_; chomp($was);
                      s/abc/def/ && print STDERR "$ARGV($.): $was : $_"' 
./foo(1): fooabcbar : foodefbar
    
por 23.10.2013 / 20:03
3

É possível usar o sinalizador w , que grava o padrão atual em um arquivo. Portanto, ao adicioná-lo ao comando substituto, podemos reportar substituições sucessivas em um arquivo e imprimi-lo depois que o trabalho é concluído. Eu também gosto de colorir a string substituída com grep.

sed -i -e "s/From/To/gw /tmp/sed.done" file_name
grep --color -e "To" /tmp/sed.done

Note que deve haver apenas um espaço entre o w e seu nome de arquivo.

Isto é ainda melhor que o diff, já que o diff também pode mostrar mudanças mesmo que elas não sejam feitas pelo sed.

    
por 05.05.2015 / 14:03
1

Eu gosto da solução @terdon - o perl é bom para isso.

Aqui está minha versão ajustada que:

  • não tentará alterar arquivos que não tenham a string correspondente
  • fará backup da versão anterior dos arquivos que são alterados (crie uma versão .bak)
  • listará todos os arquivos / lineno alterados e mostre as versões OLD e NEW dessa linha recuadas e por baixo para facilitar a leitura

código

find /tmp/test -type f ! -name "*.bak" -exec grep -l '/opt/gridmon' {} \; | xargs -L1 perl -ni'.bak' -e'$old=$_; s/\/opt\/gridmon/~/g && print STDERR "$ARGV($.):\n\tOLD:$old\tNEW:$_"'

exemplo de saída

/tmp/test/test4.cfg(13):
    OLD:    ENVFILE /opt/gridmon/server/etc/gridmonserver.cfg
    NEW:    ENVFILE ~/server/etc/gridmonserver.cfg
/tmp/test/test4.cfg(24):
    OLD:    ENVFILE /opt/gridmon/server/etc/gridmonserver.cfg
    NEW:    ENVFILE ~/server/etc/gridmonserver.cfg
    
por 04.11.2018 / 11:23