Usando um shell como o bash ou o zshell, como posso fazer um 'localizar e substituir' recursivo? Em outras palavras, quero substituir cada ocorrência de 'foo' por 'bar' em todos os arquivos neste diretório e em seus subdiretórios.
bash
shell
zsh
find-and-replace
Nathan Long
fonte
fonte
Respostas:
Este comando fará isso (testado no Mac OS X Lion e no Kubuntu Linux).
Veja como isso funciona:
find . -type f -name '*.txt'
encontra, no diretório atual (.
) e abaixo, todos os arquivos regulares (-type f
) cujos nomes terminam em.txt
|
passa a saída desse comando (uma lista de nomes de arquivos) para o próximo comandoxargs
reúne esses nomes de arquivos e os entrega um por um parased
sed -i '' -e 's/foo/bar/g'
significa "editar o arquivo no lugar, sem um backup, e fazer a seguinte substituição (s/foo/bar
) várias vezes por linha (/g
)" (Vejoman sed
)Note que a parte "sem um backup" na linha 4 está OK para mim, porque os arquivos que estou alterando estão sob controle de versão, assim eu posso facilmente desfazer se houve um erro.
fonte
-print0
opção. Seu comando falhará em arquivos com espaços, etc. em seus nomes.find -name '*.txt' -exec sed -i 's/foo/bar/g' {} +
fará tudo isso com o GNU find.sed: can't read : No such file or directory
quando eu corrofind . -name '*.md' -print0 | xargs -0 sed -i '' -e 's/ä/ä/g'
, masfind . -name '*.md' -print0
fornece uma lista de muitos arquivos.-i
e a''
''
depois desed -i
o que é''
Função?Isso remove o
xargs
dependência.fonte
sed
, então falhará na maioria dos sistemas. GNUsed
requer que você não coloque nenhum espaço entre-i
e''
.Se você está usando o Git, então você pode fazer isso:
-l
lista apenas nomes de arquivos.-z
imprime um byte nulo após cada resultado.Acabei fazendo isso porque alguns arquivos em um projeto não tinham uma nova linha no final do arquivo, e o sed adicionou uma nova linha mesmo quando não fez outras alterações. (Nenhum comentário sobre se os arquivos devem ou não ter uma nova linha no final.)
fonte
find ... -print0 | xargs -0 sed ...
As soluções não só levam muito mais tempo, mas também adicionam novas linhas aos arquivos que já não o possuem, o que é um incômodo ao trabalhar dentro de um repositório do git.git grep
é iluminação rápida por comparação.Aqui está a minha função zsh / perl que eu uso para isso:
E eu iria executá-lo usando
(por exemplo)
fonte
Experimentar:
Testado no Ubuntu 12.04.
Este comando NÃO funcionará se nomes de subdiretórios e / ou nomes de arquivos contiverem espaços, mas se você os tiver, não use este comando, pois ele não funcionará.
Geralmente, é uma prática ruim usar espaços em nomes de diretórios e nomes de arquivos.
http://linuxcommand.org/lc3_lts0020.php
Veja "Fatos importantes sobre nomes de arquivos"
fonte
Use este script de shell
Agora eu uso esse script de shell, que combina as coisas que aprendi com as outras respostas e com a pesquisa na web. Coloquei em um arquivo chamado
change
em uma pasta no meu$PATH
e fezchmod +x change
.fonte
Meu caso de uso era eu queria substituir
foo:/Drive_Letter
comfoo:/bar/baz/xyz
No meu caso, consegui fazer isso com o seguinte código. Eu estava no mesmo local de diretório onde havia muitos arquivos.Espero que tenha ajudado.
fonte
O seguinte comando funcionou bem no Ubuntu e no CentOS; no entanto, no OS X, continuei recebendo erros:
Quando tentei passar os params via xargs funcionou bem sem erros:
fonte
-i
para-i ''
é provavelmente mais relevante do que o fato de você ter mudado-exec
para-print0 | xargs -0
. BTW, você provavelmente não precisa do-e
.