Usando sed e grep / egrep para pesquisar e substituir

97

Estou usando egrep -Rseguido por uma expressão regular contendo cerca de 10 uniões, assim: .jpg | .png | .gifetc. Isso funciona bem, agora gostaria de substituir todas as strings encontradas por.bmp

Eu estava pensando em algo como

egrep -lR "\.jpg|\.png|\.gif" . | sed "s/some_expression/.jpg/" file_it_came_form

Portanto, a questão aqui é como faço para fazer uma expressão regular de união semelhante em sede como faço para salvar as alterações no arquivo de onde obteve a entrada.

Ori
fonte
2
Eu encontrei essa pergunta enquanto procurava maneiras de pesquisar e substituir vários arquivos em uma hierarquia de diretório. Para outras pessoas na minha situação, tente rpl .
titaniumdecoy
obrigado rpl funciona e é realmente fácil de lembrar .. apenas rpl old_string new_string target_files.
cesarpachon

Respostas:

183

Use este comando:

egrep -lRZ "\.jpg|\.png|\.gif" . \
    | xargs -0 -l sed -i -e 's/\.jpg\|\.gif\|\.png/.bmp/g'
  • egrep: encontre linhas correspondentes usando expressões regulares estendidas

    • -l: lista apenas nomes de arquivos correspondentes

    • -R: pesquisa recursivamente em todos os diretórios fornecidos

    • -Z: use \0como separador de registro

    • "\.jpg|\.png|\.gif": corresponde a uma das strings ".jpg", ".gif"ou".png"

    • .: inicia a pesquisa no diretório atual

  • xargs: executa um comando com o stdin como argumento

    • -0: use \0como separador de registro. Isso é importante para corresponder ao -Zde egrepe para evitar ser enganado por espaços e novas linhas nos nomes dos arquivos de entrada.

    • -l: use uma linha por comando como parâmetro

  • sed: O s tream ed itor

    • -i: substitui o arquivo de entrada com a saída sem fazer um backup

    • -e: use o seguinte argumento como expressão

    • 's/\.jpg\|\.gif\|\.png/.bmp/g': substitua todas as ocorrências das strings ".jpg", ".gif"ou ".png"por".bmp"

David Schmitt
fonte
tudo funciona, exceto o | na parte sed. Eu não entendo porque, já que faz sentido ... a parte -l do xargs estava me dando erros, então eu o tirei, isso poderia estar relacionado?
Ori
Descobri que este comando adiciona uma nova linha ao final de todos os arquivos que ele processa.
titaniumdecoy
@titanumdecoy: Não consegui reproduzir esse comportamento. qual versão do sed você estava usando e em qual sistema operacional você está?
David Schmitt de
1
@DavidSchmitt: Você provavelmente deseja usar sed -rpara expressões regulares estendidas. Nesse ponto, o padrão corresponderá ao que é usado em egrep e você pode querer colocá-lo em uma variável para reutilização.
bukzor
este comando me poupou horas de trabalho copiando arquivos de cabeçalho de meu aplicativo para a biblioteca que fiz. Isso é incrível :) Aqui está o comando que usei egrep -lRZ "\ .h $". | xargs -0 tar -cvf headers.tar | (cabeçalhos cp headers.tar; cabeçalhos de cd; tar xf headers.tar;)
The Lazy Coder
11

Honestamente, por mais que eu ame o sed para as tarefas apropriadas, esta é definitivamente uma tarefa para o perl - é verdadeiramente mais poderoso para este tipo de frases curtas, especialmente para "escrever de volta para onde ele vem" (o -iswitch do perl faz isso para você e, opcionalmente, também permite que você mantenha a versão antiga, por exemplo, com um .bak anexado, basta usar no -i.baklugar).

perl -i.bak -pe 's/\.jpg|\.png|\.gif/.jpg/

ao invés de trabalhos complexos em sed (se é que é possível lá) ou awk ...

Alex Martelli
fonte
6
sed usa -i, assim como perl.
Stobor
@Stobor - Juro que tive problemas em que a operação perl quando alimentei a string de substituição de regex fez exatamente o que eu queria, ao contrário do sed, mesmo se eu tivesse dado a opção de regex para sed.. Acho que esqueci alguns sinalizadores para sed ou tinha limitações.
meder omuraliev
10

Outra maneira de fazer isso

find . -name *.xml -exec sed -i "s/4.6.0-SNAPSHOT/5.0.0-SNAPSHOT/" {} \;

Alguma ajuda em relação ao comando acima

O find fará o find para você no diretório atual indicado por .

-name o nome do arquivo no meu caso seu pom.xml pode dar curingas.

-exec executar

sed editor de fluxo

-i ignorar caso

s é para substituto

/4.6.0.../ String a ser pesquisada

/5.0.0.../ String a ser substituída

Cyril Cherian
fonte
talvez não tão poderoso - mas isso é muito mais fácil para mim entender do que a resposta aceita
icc97
5

Não consegui fazer nenhum dos comandos desta página funcionarem para mim: a solução sed adicionou uma nova linha ao final de todos os arquivos que processou e a solução perl não foi capaz de aceitar argumentos suficientes de find. Encontrei esta solução que funciona perfeitamente:

find . -type f -name '*.[hm]' -print0 
    | xargs -0 perl -pi -e 's/search_regex/replacement_string/g'

Isso irá percorrer novamente a árvore do diretório atual e substituí-lo search_regexpor replacement_stringquaisquer arquivos que terminem em .hou.m .

Eu também usei rpl para esse propósito no passado.

titaniumdecoy
fonte
0

tente algo usando um loop for

 for i in `egrep -lR "YOURSEARCH" .` ; do echo  $i; sed 's/f/k/' <$i >/tmp/`basename $i`; mv /tmp/`basename $i` $i; done

não é bonito, mas deve servir.

Don Johe
fonte
3
xargs é definitivamente mais apropriado aqui.
Nathan Fellman
1
e usar o |while read ipadrão permitiria streaming e evitaria restrições de comprimento quando os resultados de egrep se tornassem muito longos
David Schmitt,
0

Meu caso de uso era que eu queria substituir foo:/Drive_Letterpor. foo:/bar/baz/xyz No meu caso, consegui fazer isso com o código a seguir. Eu estava no mesmo diretório onde havia grande quantidade de arquivos.

find . -name "*.library" -print0 | xargs -0 sed -i '' -e 's/foo:\/Drive_Letter:/foo:\/bar\/baz\/xyz/g'

espero que tenha ajudado.

ATUALIZAÇÃO s | foo: / Drive_letter: | foo: / ba / baz / xyz | g

neo7
fonte
Você pode usar outros delimitadores para o comando sed, e isso torna os nomes de caminho muito mais agradáveis:sed 's|foo:/Drive_letter:|foo:/ba/baz/xyz|g'
Kevin