*nix One-Liner: Encontre arquivos recursivamente por extensão e copie-os para um novo diretório nomeado para a extensão

*nix One-Liner: Encontre arquivos recursivamente por extensão e copie-os para um novo diretório nomeado para a extensão

Dados muitos diretórios/subdiretórios contendo muitos tipos de arquivos diferentes (csv, txt, jpg, etc.), qual comando pesquisará cada diretório recursivamente e copiará os arquivos em um novo diretório de nível superior nomeado após cada extensão.

Existe uma maneira de conseguir isso com uma linha?

Responder1

Solução

O comando a seguir listará todos os arquivos no diretório atual ( .) e os moverá para diretórios nomeados de acordo com suas extensões ./OUTPUT:

for file in $(find . -type f) ; do EXTENSION=$(echo "$file" |sed 's/.*\.//g') ; rsync -av $file ./OUTPUT/$EXTENSION/ ; done

Antes de executar o comando acima, você precisará:

  • Crie o diretório OUTPUT no diretório atual ( mkdir OUTPUT)
  • Certifique-se de que não haja arquivos com nomes correspondentes em nenhum dos subdiretórios

Explicação

Primeiramente, o loop atribui à variável fileum arquivo da saída de find . -type f. Exemplo de saída do findcomando está abaixo:

./a/myfile1.zip
./a/b/adoc1.txt
./a/b/adoc2.txt
./a/b/adoc3.txt
./a/b/adoc4.txt
./a/myfile3.zip
./a/myfile4.zip
./a/myfile2.zip

A variável EXTENSIONé criada e seu valor é a filevariável com tudo até o último ponto removido. Assim ./a/myfile1.zipse tornaria zip.

O arquivo é então sincronizado (com detalhamento v) para um diretório OUTPUTnomeado após a extensão do arquivo.

Um exemplo da minha árvore de arquivos após executar este comando em algum teste:

./a
./a/b
./a/b/adoc1.txt
./a/b/adoc2.txt
./a/b/adoc3.txt
./a/b/adoc4.txt
./a/b/c
./a/myfile1.zip
./a/myfile2.zip
./a/myfile3.zip
./a/myfile4.zip
./b
./b/c
./b/c/d
./b/c/file1.txt
./b/c/file2.txt
./b/c/file3.txt
./b/c/file4.txt
./c
./c/d
./c/d/e
./c/d/e/afile1.rar
./c/d/e/afile2.rar
./c/d/e/afile3.rar
./c/d/e/afile4.rar
./OUTPUT
./OUTPUT/rar
./OUTPUT/rar/afile1.rar
./OUTPUT/rar/afile2.rar
./OUTPUT/rar/afile3.rar
./OUTPUT/rar/afile4.rar
./OUTPUT/txt
./OUTPUT/txt/adoc1.txt
./OUTPUT/txt/adoc2.txt
./OUTPUT/txt/adoc3.txt
./OUTPUT/txt/adoc4.txt
./OUTPUT/txt/file1.txt
./OUTPUT/txt/file2.txt
./OUTPUT/txt/file3.txt
./OUTPUT/txt/file4.txt
./OUTPUT/zip
./OUTPUT/zip/myfile1.zip
./OUTPUT/zip/myfile2.zip
./OUTPUT/zip/myfile3.zip
./OUTPUT/zip/myfile4.zip

Responder2

Isto pode ser feito de forma muito simples comencontrar, conforme sugerido corretamente por Alex: assumindo que o diretório para procurar arquivos é/caminho1/para/algum/dire você deseja que os arquivos sejam movidos para/caminho2/para/outro/dir/txte assim por diante,

find /path1/some/dir -type f -exec bash -c 'MYDIR=/path2/to/other/dir${0##*.}; [ -d $MYDIR ] && mv $0 $MYDIR  || mv $0 /path3/to/miscellanea' {} \; 

Este comando primeiro encontra todos os arquivos (-tipo f) contido no diretório/caminho1/para/algum/dire todos os seus subdiretórios, então passa o nome de cada arquivo ({}) para um pequeno bash de duas linhas, que primeiro extrai a extensão do nome do arquivo (referido comoUS$ 0dentro do bash) e o atribui à variável shell localMEUDIR, entãotestespela existência de um diretório adequado (se não existir tal diretório, talvez tenhamos cometido um erro) e se o diretório existir (&& ...) ele move o arquivo para o diretório apropriado, caso contrário (||...) coloca o arquivo em um local padrão,/caminho3/para/diversos.

O acima pressupõe que você já criou os diretórios necessários,/caminho2/para/outro/dir/txte assim por diante. Se, em vez disso, você quiser criá-los em qualquer lugar, você terá que modificar o acima como:

find /path1/some/dir -type f -exec bash -c 'MYDIR=/path2/to/other/dir/${0##*.}; mkdir -p $MYDIR && mv $0 $MYDIR' {} \; 

informação relacionada