Como grep e substituir


251

Eu preciso procurar recursivamente por uma seqüência especificada dentro de todos os arquivos e subdiretórios dentro de um diretório e substituí-la por outra.

Eu sei que o comando para encontrá-lo pode ficar assim:

grep 'string_to_find' -r ./*

Mas como posso substituir todas as instâncias string_to_findpor outra string?


Não acredito que o grep possa fazer isso (posso estar errado). Maneiras mais fáceis seria usar sed ou Perl para fazer a substituição
Memento Mori

2
Tente usarsed -i 's/.*substring.*/replace/'
Eddy_Em

2
@ Eddy_Em Isso substituirá a linha inteira por replace. Você precisa usar o agrupamento para capturar a parte da linha antes e depois da substring e depois colocá-la na linha de substituição. sed -i 's/\(.*\)substring\(.*\)/\1replace\2/'
JStrahl


Respostas:


248

Outra opção é usar o find e passá-lo pelo sed.

find /path/to/files -type f -exec sed -i 's/oldstring/new string/g' {} \;

34
No OS X 10.10 Terminal, -ié necessária uma sequência de extensão adequada para o parâmetro . Por exemplo, find /path/to/files -type f -exec sed -i "" "s/oldstring/new string/g" {} \;qualquer forma, proporcionando string vazia ainda cria um arquivo de backup ao contrário descritas no manual ...
Eonil

10
Por que obtenho "erro sed: RE: sequência de bytes ilegal". E sim, eu adicionei o -i ""para o OS X. Funciona de outra forma.
taco

2
Eu tive o problema de sequência ilegal de bytes no macOS 10.12, e esta pergunta / resposta resolveu meu problema: stackoverflow.com/questions/19242275/… .
Abeboparebop

3
Isso toca em todos os arquivos para que os horários sejam modificados; e converte terminações de linha de CRLFpara LFno Windows.
quer tocar hoje

183

Eu tenho a resposta.

grep -rl matchstring somedir/ | xargs sed -i 's/string1/string2/g'

14
Isso examinaria os arquivos correspondentes duas vezes ... uma vez grepe novamente com sed. Usar o findmétodo é mais eficiente, mas esse método que você mencionou funciona.
Cmevoli

41
No OS X, você precisará mudar sed -i 's/str1/str2/g'para sed -i "" 's/str1/str2/g'para que isso funcione.
jdf

6
@cmevoli com esse método, greppercorre todos os arquivos e sedverifica apenas os arquivos correspondentes grep. Com o findmétodo na outra resposta, findprimeiro lista todos os arquivos e, em seguida sed, varre todos os arquivos desse diretório. Portanto, esse método não é necessariamente mais lento, depende de quantas correspondências existem e das diferenças nas velocidades de pesquisa entre sed, grepe find.
Joelostblom

4
OTOH desta forma permite que você visualize o grep achados antes de realmente substituir, reduzindo o risco de falha muito, especialmente para n00bs regex como eu
Lennart Rolland

2
Isso também é útil quando a substituição do grep é mais inteligente que o sed. Por exemplo, o ripgrep obedece ao .gitignore enquanto o sed não.
user31389

43

Você poderia até fazer assim:

Exemplo

grep -rl 'windows' ./ | xargs sed -i 's/windows/linux/g'

Isso procurará pela string ' windows ' em todos os arquivos relativos ao diretório atual e substituirá ' windows ' por ' linux ' para cada ocorrência da string em cada arquivo.


2
A grepsó é útil se houver arquivos que não devem ser modificados. A execução sedde todos os arquivos atualizará a data de modificação do arquivo, mas manterá o conteúdo inalterado se não houver correspondências.
tripleee

@ tripleee: Cuidado com ... mas [sed] deixa o conteúdo inalterado se não houver correspondências " . Ao usar -i, acredito que sedaltera o tempo do arquivo de todos os arquivos em que ele toca, mesmo que o conteúdo não seja alterado. sedtambém converte as terminações de linha . Eu não uso sedno Windows em um repositório git, porque todos CRLFsão alteradas para LF.
JWW

Este comando precisa "" depois do -i para indicar que nenhum arquivo de backup será criado após a substituição no local, pelo menos no macosx. Verifique a página do manual para obter detalhes. Se você deseja um backup, é aqui que você coloca a extensão do arquivo a ser criado.
spinyBabbler 16/04

31

Isso funciona melhor para mim no OS X:

grep -r -l 'searchtext' . | sort | uniq | xargs perl -e "s/matchtext/replacetext/" -pi

Fonte: http://www.praj.com.au/post/23691181208/grep-replace-text-string-in-files


isto é perfeito! também trabalha com ag:ag "search" -l -r . | sort | uniq | xargs perl -e 's/search/replace' -pi

@sebastiankeller Seu comando Perl não possui a barra final, que é um erro de sintaxe.
tripleee

3
Por que a sort -uparte uniforme disso? Em que circunstâncias você esperaria grep -rlproduzir o mesmo nome de arquivo duas vezes?
tripleee

5

Outras soluções combinam sintaxes regex. Para usar padrões de perl / PCRE para ambos pesquisa e substituição, e somente os arquivos de correspondência de processos, isso funciona muito bem:

grep -rlZPi 'match1' | xargs -0r perl -pi -e 's/match2/replace/gi;'

onde match1e match2geralmente são idênticos, mas match1podem ser simplificados para remover recursos mais avançados que são relevantes apenas para a substituição, por exemplo, grupos de captura.

Tradução: greprecursivamente e liste os arquivos que correspondem a esse padrão PCRE, separados por nul para proteger caracteres especiais no nome do arquivo e canalize os nomes de arquivos nos xargsquais está esperando uma lista separada por nulos, mas não fará nada se nenhum nome for recebido, e perlsubstitua as linhas onde as correspondências são encontradas.

Adicione a Iopção greppara ignorar arquivos binários. Para correspondência com distinção entre maiúsculas e minúsculas, solte o icomutador de grepe o isinalizador anexado à expressão de substituição, mas não o icomutador em perlsi.


O próprio Perl é bastante capaz de recuperar uma estrutura de arquivo. De fato, existe uma ferramenta find2perlque acompanha o Perl que faz esse tipo de coisa sem nenhum xargstruque.
tripleee

O @tripleee findnão pesquisa o conteúdo do arquivo, e o objetivo é processar apenas os arquivos correspondentes sem escrever um programa Perl.
Walf

Essa é uma boa solução para o Windows, pois evita o problema das soluções baseadas em sed de converter as terminações de linha. Obrigado!
JamHandy

4

Geralmente não com grep, mas com sed -i 's/string_to_find/another_string/g'ou perl -i.bak -pe 's/string_to_find/another_string/g'.


3

Tenha muito cuidado ao usar finde sedem um repositório git! Se você não excluir os arquivos binários, poderá acabar com este erro:

error: bad index file sha1 signature 
fatal: index file corrupt

Para resolver esse erro, você precisa reverter o sedseu, substituindo o seu new_stringpelo seu old_string. Isso reverterá as seqüências substituídas, e você voltará ao início do problema.

A maneira correta de procurar por uma string e substituí-la é pular finde usar greppara ignorar os arquivos binários:

sed -ri -e "s/old_string/new_string/g" $(grep -Elr --binary-files=without-match "old_string" "/files_dir")

Créditos para @hobs


1

Aqui está o que eu faria:

find /path/to/dir -type f -iname "*filename*" -print0 | xargs -0 sed -i '/searchstring/s/old/new/g'

isso procurará todos os arquivos que contenham filenameo nome do arquivo, abaixo de /path/to/dir, para cada arquivo encontrado, procure a linha com searchstringe substitua oldpor new.

Porém, se você deseja omitir a procura de um arquivo específico com uma filenamestring no nome do arquivo, simplesmente faça:

find /path/to/dir -type f -print0 | xargs -0 sed -i '/searchstring/s/old/new/g'

Isso fará o mesmo acima, mas para todos os arquivos encontrados em /path/to/dir.


0

Outra opção seria usar apenas perl com a globstar.

A ativação shopt -s globstarem seu .bashrc(ou em qualquer outro lugar) permite que o **padrão global corresponda a todos os subdiretórios e arquivos de forma recursiva.

Assim, o uso perl -pXe 's/SEARCH/REPLACE/g' -i **substituirá recursivamente SEARCHpor REPLACE.

O -Xsinalizador diz ao perl para "desativar todos os avisos" - o que significa que não se queixará de diretórios.

O globstar também permite que você faça coisas como sed -i 's/SEARCH/REPLACE/g' **/*.extse você queria substituir SEARCHcom REPLACEem todos os arquivos da criança com a extensão .ext.


"Outra opção seria usar o perl apenas com a globstar ..." - Não em máquinas Posixy, como o Solaris. É por isso que estou procurando especificamente grepe sed.
JWW
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.