Substituição rápida de strings em diretórios recursivos

0

Como posso fazer um rápido texto substituir com 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 bem conhecido.

Usar find é extremamente lento para muitos arquivos, porque gera um novo processo para cada arquivo, então estou procurando uma maneira que tenha a passagem de diretórios e a substituição de strings 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 dois passos e não suporta espaços.)

perl -p -i -e 's/foo/bar/g' 'grep -lr --include=*.txt foo'
    
por forthrin 29.03.2018 / 20:28

3 respostas

5

Você só deseja usar o:

 find . -name '*.txt'  -exec cmd {} \;

formulário para os cmd s que podem receber apenas um argumento. Esse não é o caso de grep . Com grep :

 find . -name '*.txt'  -exec grep foo /dev/null {} +

(ou use -H com GNU grep ). Mais sobre isso em greping recursivo vs find / -tipo f -exec grep {} \; Qual é mais eficiente / mais rápido?

Agora, para substituição, é o mesmo, perl -pi pode levar mais de um argumento:

 find . -name '*.txt' -type f -exec perl -pi -e s/foo/bar/g {} +

Agora, isso seria reescrever os arquivos independentemente de conterem foo ou não. Em vez disso, você pode querer (assumindo o GNU grep e xargs ou compatível):

 find . -name '*.txt' -type f -exec grep -l --null foo {} + |
   xargs -r0 perl -pi -e s/foo/bar/g

Ou:

 grep -lr --null --include='*.txt' foo . |
   xargs -r0 perl -pi -e s/foo/bar/g

Portanto, apenas os arquivos que contêm foo serão reescritos.

BTW, --include=*.txt ( --include sendo outra extensão do GNU) é um shell glob, portanto, deve ser citado. Por exemplo, se houvesse um arquivo chamado --include=foo.txt no diretório atual, o shell expandiria --include=*.txt para isso antes de chamar grep . E, se não, com muitos shells, você receberia um erro sobre a falha do glob em corresponder a qualquer arquivo.

Então você quer o grep --include='*.txt'

    
por 29.03.2018 / 21:55
2

Quando sua expressão find é tão simples, você pode usar seu shell para fazer o globbing. O limite principal que você poderia encontrar seria lidar com mais arquivos do que caberia em uma linha de comando.

Um exemplo no bash:

$ shopt -s globstar

$ date > a.txt
$ date > b.txt
$ date > c.txt
$ cat *.txt
Thu Mar 29 14:57:57 EDT 2018
Thu Mar 29 14:58:00 EDT 2018
Thu Mar 29 14:58:02 EDT 2018
$ mkdir -p deep/sub/dir
$ mv *.txt deep/sub/dir

$ perl -pi -e 's/EDT/EST/' **/*.txt

$ cat deep/sub/dir/*.txt
Thu Mar 29 14:57:57 EST 2018
Thu Mar 29 14:58:00 EST 2018
Thu Mar 29 14:58:02 EST 2018
    
por 29.03.2018 / 21:01
0

Você pode usar find ... -exec com um terminador "+" em vez de ";" para executar arquivos em lotes grandes em vez de um por vez (desde que o comando que está sendo exec ed suporte vários arquivos em uma chamada):

find . -name '*.txt' -exec grep foo {} +
find . -name '*.txt' -exec perl -i -pe 's/foo/bar/' {} +
    
por 29.03.2018 / 21:55