Como posso substituir um texto rápido por diretórios recursivos e nomes de arquivos com espaços e aspas simples ? De preferência, usando ferramentas padrão do UNIX ou, alternativamente, um pacote conhecido.
O uso find
é extremamente lento para muitos arquivos, porque gera um novo processo para cada arquivo, então estou procurando uma maneira que tenha passagem de diretório e substituição de string integrada como uma operação.
Pesquisa lenta:
find . -name '*.txt' -exec grep foo {} \;
Pesquisa rápida:
grep -lr --include=*.txt foo
Substituição lenta:
find . -name '*.txt' -exec perl -i -pe 's/foo/bar/' {} \;
Substituição rápida:
# Your suggestion here
(Este é bastante rápido, mas é de duas passagens e não lida com espaços.)
perl -p -i -e 's/foo/bar/g' `grep -lr --include=*.txt foo`
Você só gostaria de usar o:
formulário para aqueles
cmd
s que podem ter apenas um argumento. Não é o caso degrep
. Comgrep
:(ou use
-H
com GNUgrep
). Mais sobre isso em Recursive grep vs find / -type f -exec grep {} \; Qual é mais eficiente/rápido?Agora, para substituição, é a mesma coisa,
perl -pi
pode levar mais de um argumento:Agora, isso reescreveria os arquivos, independentemente de eles conterem
foo
ou não. Em vez disso, você pode querer (assumindo GNUgrep
exargs
/ou compatível):Ou:
Portanto, apenas os arquivos que contêm
foo
serão reescritos.BTW,
--include=*.txt
(--include
sendo outra extensão GNU) é um shell glob, então deve ser citado. Por exemplo, se houvesse um arquivo chamado--include=foo.txt
no diretório atual, o shell se expandiria--include=*.txt
antes de chamargrep
. E se não, com muitos shells, você obteria um erro sobre o glob não corresponder a nenhum arquivo.Então você iria querer
grep --include='*.txt'
Quando sua
find
expressão é tão simples, você pode usar seu shell para fazer o globbing. O principal limite que você poderia enfrentar seria lidar com mais arquivos do que cabem em uma linha de comando.Um exemplo no bash:
Você pode usar
find ... -exec
com um terminador "+" em vez de ";" para executar arquivos em grandes lotes em vez de um de cada vez (desde que o comando sendoexec
ed suporte vários arquivos em uma chamada):