find -exec cmd {} + vs | xargs

115

Qual deles é mais eficiente em um conjunto muito grande de arquivos e deve ser usado?

find . -exec cmd {} +

ou

find . | xargs cmd

(Suponha que não haja personagens engraçados nos nomes dos arquivos)

dogbane
fonte
Relacionado: stackoverflow.com/questions/9612090/…
Mateusz Piotrowski

Respostas:

107

A diferença de velocidade será insignificante.

Mas você tem que ter certeza de que:

  1. Seu script não assumirá que nenhum arquivo terá espaço, tabulação, etc no nome do arquivo; a primeira versão é segura, a segunda não.

  2. Seu script não tratará um arquivo que comece com " -" como uma opção.

Portanto, seu código deve ser assim:

find . -exec cmd -option1 -option2 -- {} +

ou

find . -print0 | xargs -0 cmd -option1 -option2 --

A primeira versão é mais curta e fácil de escrever, pois você pode ignorar 1, mas a segunda versão é mais portável e segura, já que " -exec cmd {} +" é uma opção relativamente nova no GNU findutils (desde 2005, muitos sistemas em execução ainda não a terão) e estava cheio de erros recentemente . Além disso, muitas pessoas não sabem disso " -exec cmd {} +", como você pode ver nas outras respostas.

Tometzky
fonte
4
-print0 também é uma opção GNU find (e GNU xargs) que está faltando em muitos sistemas não Linux, então o argumento de portabilidade não é tão válido. Usar apenas -print e deixar o -0 de fora do xargs, entretanto, é muito portátil.
dannysauer
7
A questão é que sem -print0 não funciona se houver um arquivo com um espaço ou guia etc. Isso pode ser uma vulnerabilidade de segurança, pois se houver um nome de arquivo como "foo -o index.html" então -o será tratado como opção. Tente no diretório vazio: "touch - foo \ -o \ index.html; find. | Xargs cat". Você receberá: "cat: invalid option - 'o'"
Tometzky
2
Seu exemplo é um nome de arquivo que contém um -. Sem -print0, find irá cuspir ./foo -o index.html. Portanto, talvez começar com - não seja grande coisa, mas o resultado é pouco alterado e, em um sistema multiusuário, pode fornecer um vetor de ataque se seu script for legível por todos.
bobpaul
2
Uma observação sobre algo que me atrapalhou aqui - o uso de execproduzirá resultados conforme forem encontrados, onde xargs, ao que parece, esperará até que todo o diretório seja pesquisado antes de gravar em stdout. Se você está tentando fazer isso em um diretório grande e parece que xargsnão está funcionando, recomenda-se paciência.
FarmerGedden de
1
@Motivated Sem -print0find retorna nomes de arquivos separados por nova linha, mas nova linha também pode fazer parte de um nome de arquivo, tornando-o ambíguo. O byte 0 não pode, por isso é um separador seguro. Sim - adicionar --um comando que o suporte é uma boa prática quando você não pode controlar seus argumentos, mesmo que nem sempre seja estritamente necessário ou não seja seguro.
Tometzky
7
find . | xargs cmd

é mais eficiente (ele roda o menor cmdnúmero de vezes possível, ao contrário exec, que rodacmd uma vez para cada partida). No entanto, você terá problemas se os nomes dos arquivos contiverem espaços ou caracteres estranhos.

O seguinte é sugerido para ser usado:

find . -print0 | xargs -0 cmd

isso vai funcionar mesmo se os nomes de arquivo contêm caracteres funk ( -print0marcas findimprimir partidas NUL-terminadas, -0marcas xargsesperam que este formato.)

ASk
fonte
28
Isso não é "find. -Exec cmd {} \;" mas "find. -exec cmd {} +". Este último não executará um arquivo de cada vez.
Tometzky
2
Observe que a xargsabordagem é significativamente mais lenta se não houver (ou apenas alguns) arquivos correspondentes e cmdnão houver muito o que fazer para cada arquivo. Por exemplo, quando executado em um diretório vazio, a xargsversão levará pelo menos o dobro do tempo, pois dois processos devem ser iniciados em vez de apenas um. (Sim, a diferença geralmente é imperceptível em * nix, mas em um loop pode ser importante; ou, tente no Windows em algum momento ...)
SamB
2

As xargsversões modernas geralmente oferecem suporte à execução de pipeline paralela.

Obviamente, pode ser um ponto central quando se trata de escolher entre find … -exec e … | xargs

poige
fonte