*nix One-Liner: Рекурсивный поиск файлов по расширению и копирование их в новый каталог, названный по расширению

*nix One-Liner: Рекурсивный поиск файлов по расширению и копирование их в новый каталог, названный по расширению

Учитывая, что существует множество каталогов/подкаталогов, содержащих множество различных типов файлов (csv, txt, jpg и т. д.), какая команда будет рекурсивно искать в каждом каталоге и копировать файлы в новый каталог верхнего уровня, названный в честь каждого расширения?

Есть ли способ сделать это с помощью одной строки?

решение1

Решение

Следующая команда выведет список всех файлов в текущем каталоге ( .) и переместит их в каталоги, названные по их расширениям в ./OUTPUT:

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

Перед выполнением указанной выше команды вам необходимо:

  • Создать каталог OUTPUT в текущем каталоге ( mkdir OUTPUT)
  • Убедитесь, что ни в одном из подкаталогов нет файлов с совпадающими именами.

Объяснение

Во-первых, цикл присваивает переменной fileфайл из вывода find . -type f. Пример вывода команды findприведен ниже:

./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

Переменная EXTENSIONсоздается, и ее значением является fileпеременная, в которой удалено все до последней точки. Так что это ./a/myfile1.zipстанет zip.

Затем файл синхронизируется (с подробностями v) с каталогом, OUTPUTназванным по расширению файла.

Пример моего файлового дерева после запуска этой команды в некотором тесте:

./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

решение2

Это можно сделать очень просто с помощьюнаходить, как правильно предположил Алекс: предположим, что каталог для сканирования файлов - это/путь1/к/какой-то/директории, и вы хотите переместить файлы в/путь2/к/другой/директории/txtи так далее,

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' {} \; 

Эта команда сначала находит все файлы (-тип f) содержится в каталоге/путь1/к/какой-то/директориии все его подкаталоги, затем передает имя каждого такого файла ({}) в небольшую двухстрочную программу bash, которая сначала извлекает расширение из имени файла (называется$0внутри bash) и присваивает его локальной переменной оболочкиМЫДИР, затемтестына предмет наличия подходящего каталога (если такого каталога нет, то, возможно, мы ошиблись), а также, если каталог существует (&& ...) он перемещает файл в соответствующий каталог, в противном случае (||...) он помещает файл в место по умолчанию,/path3/to/разное.

Вышеизложенное предполагает, что вы уже создали необходимые каталоги,/путь2/к/другой/директории/txtи т. д. Если вместо этого вы хотите создавать их на ходу, вам придется изменить вышеприведенное следующим образом:

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

Связанный контент