Como integrar o comando mv após o comando find?

61

Estou procurando arquivos cujo nome contenha AAAem seu caminho usando o seguinte comando:

find path_A -name "*AAA*"

Dada a saída mostrada pelo comando acima, quero mover esses arquivos para outro caminho, digamos path_B. Em vez de mover esses arquivos um por um, posso otimizar o comando movendo esses arquivos logo após o comando find?

huahsin68
fonte

Respostas:

87

Com o GNU mv :

find path_A -name '*AAA*' -exec mv -t path_B {} +

Isso usará a -execopção find, que substitui o {}resultado de cada localização por vez e executa o comando que você fornece. Como explicado em man find:

   -exec command ;
          Execute  command;  true  if 0 status is returned.  All following
          arguments to find are taken to be arguments to the command until
          an  argument  consisting of `;' is encountered.  

Nesse caso, estamos usando a +versão -execpara executarmos o menor número mvpossível de operações:

   -exec command {} +
          This  variant  of the -exec action runs the specified command on
          the selected files, but the command line is built  by  appending
          each  selected file name at the end; the total number of invoca‐
          tions of the command will  be  much  less  than  the  number  of
          matched  files.   The command line is built in much the same way
          that xargs builds its command lines.  Only one instance of  `{}'
          is  allowed  within the command.  The command is executed in the
          starting directory.
cuonglm
fonte
Não esqueça o "\;" no final do exec!
Charles Roth
@Charles Roth: é +que dows o trabalho, você pode ler a minha citação acima ou man findem vez
cuonglm
Obrigado por isso! Eu estava tentando usar -exec mv {} path_b +e estava falhando com erros de permissão. TBH, eu ainda não entendo o porquê, mas -exec mv -t path_b {} +funciona um prazer!
Jeremy Davis
6
@JeremyDavis Ao usar -exec ... {} +, {}tem que ser a última coisa antes da +. É por isso que ele usa mv -t destdir {} +e não mv {} destdir +. Um resfriado foi usado -exec mv {} destdir ';', mas isso seria executado mvuma vez para cada arquivo.
Kusalananda
23

Você poderia fazer algo como abaixo também.

find path_A -name "*AAA*" -print0 | xargs -0 -I {} mv {} path_B

Onde,

  1. -0Se houver espaços em branco ou caracteres (incluindo novas linhas), muitos comandos não funcionarão. Esta opção cuida dos nomes dos arquivos com espaço em branco.
  2. -ISubstitua ocorrências de replace-str nos argumentos iniciais pelos nomes lidos na entrada padrão. Além disso, espaços em branco não citados não finalizam itens de entrada; em vez disso, o separador é o caractere de nova linha.

Testando

Eu criei dois diretórios como sourcedire destdir. Agora, eu criei monte de arquivos dentro sourcedirde file1.bak, file2.bakefile3 with spaces.bak

Agora, eu executei o comando como,

find . -name "*.bak" -print0 | xargs -0 -I {} mv {} /destdir/

Agora, dentro do destdir, quando eu faço ls, pude ver que os arquivos foram movidos de sourcedirpara destdir.

Referências

http://www.cyberciti.biz/faq/linux-unix-bsd-xargs-construct-argument-lists-utility/

Ramesh
fonte
22

Para o benefício dos usuários do OS X que se deparam com essa questão, a sintaxe no OS X é um pouco diferente. Supondo que você não deseja procurar recursivamente em subdiretórios de path_A:

find path_A -maxdepth 1 -name "*AAA*" -exec mv {} path_B \;

Se você deseja pesquisar todos os arquivos recursivamente em path_A:

find path_A -name "*AAA*" -exec mv {} path_B \;
mannykary
fonte
Esta não é uma sintaxe específica do OS X, é a mesma com qualquer outra findque eu usei. Pontos -maxdepthpositivos : (especialmente se path_B é um subdiretório - evita mvtentar mover arquivos que já estão lá!) E usar \; (então {} não precisa ser o último parâmetro e a mvsintaxe normal pode ser usada)
drevicko
6

A -execé a melhor maneira de fazer isso. Se, por qualquer motivo, isso não for uma opção, você também poderá ler os resultados em um loop:

find path_A -name "*AAA*" -print0 | 
    while IFS= read -r -d $'\0' file; do mv "$file" path_B; done

Essa é a maneira segura: ele pode lidar com nomes de arquivos que contêm espaços, novas linhas ou outros caracteres estranhos. Uma maneira mais simples, mas que falhará, a menos que os nomes dos arquivos consistam apenas em caracteres alfanuméricos simples , é

mv $(find path_A -name "*AAA*") path_B

Mas use o loop while.

terdon
fonte
A maneira mais simples ainda pode falhar se your file names consist only of simple alphanumeric characters, por exemplo, se chegarARG_MAX