Pull to refresh

Как правильно скопировать файлы и папки исключая некоторые из них

Reading time 2 min
Views 49K
Топик написан в ответ на похожий.

Автор оригинального топика предлагает решить задачу в лоб — а именно, скопировать все файлы а потом удалить не нужные. Это может быть неплохим решением, если вам, конечно, не нужно скопировать всю домашнюю папку на флешку, за исключением вашей коллекции видео.

Но главная проблема этого подхода в другом — он не соответствует идеологии unix: сложные задачи решаются комбинацией простых утилит.

Под катом подробности о методах решения этого класса задач — не рассматривайте это как готовый рецепт.


0. Декомпозиция


Решение любой комплексной задачи начинается с разбора её на составные части. Итак нам нужно скопировать некоторый набор файлов предварительно его отфильтровав.
Значит — получение списка файлов, фильтрация, копирование.

1. Получение списка файлов



Обычно мы просматриваем список файлов программой ls. Её вывод выглядит примерно так:
$ ls -1
dir1
dir2
file1.bin
file2.txt

Подходит ли там такой вывод? Нет, потому, что в нем недостаточно информации — нам нужно копировать файлы рекурсивно, значит для нас было-бы гораздо удобнее если первая в нашей цепочке программа выдала там имена файлов вместе с путями.

Следующая программа, которая приходит на ум — find
$ find ./
./
./dir1
./dir1/file7.txt
./dir2
./file1.bin
./file2.txt

Уже лучше но в вывод попали и директории, а они нам не нужны. Попробуем так:
$ find ./ -type 'f'
./dir1/file7.txt
./file1.bin
./file2.txt


Вот то, что там нужно. Список файлов.

2. Фильтрация



Этот список файлов нужно отфильтровать. Перенаправим вывод нашей предыдущей комманды в программу grep.
$ find ./ -type 'f' | grep 2
./dir2
./file2.txt


Хорошо, но в условиях задачи стоит исключать файлы, так что немного поменеяем наш конвейер
$ find ./ -type 'f' | grep -v 2
./dir1/file7.txt
./file1.bin


Первые две части выполнены.

3. Копирование



Из man-страницы для команды cp мы можем узнать, что исходный файл нужно передавать программе cp в качестве аргумента, а мы пока можем только перенаправить список на стандартный ввод.
Применим утилиту xargs — она принимает стандартный ввод и вызывает указанную программу с параметрами из стандартного ввода. Итак:
$  find ./ -type 'f' | grep -v 2 | xargs -n 1 -I % cp --parents  "%"  /path/to/dest/dir/

-n 1 значит, что только одна строка из стандартного ввода подставляется в комманду, а -I % — определяет символ, который будет заменен в целевой комманде на строчку из стандартного ввода. В нашем случае это будет
 cp --parents  "./dir1/file7.txt"  /path/to/dest/dir/
 cp --parents  "./file1.bin"  /path/to/dest/dir/


Можно считать, что задача решена.

Вместо заключения


Я надеюсь что это описание поможет правильно подходить к решению как таких простых так и более комплексных задач.

Хочется отметить, что
  • Это топик способах решения задач и немного о применении конвейера, а не о копировании файлов
  • Этот способ далеко не едиственный и даже не самый короткий, а наиболее наглядный для демонстрации методологии решения.
  • В случае этой конкретной задачи будет быстрее воспользоваться find ./ -type f ! -name "*2*" -exec cp --parents -t /target/dir "{}" \+
  • Лично я воспользовался-бы tar --exclude=2 -cf - ./ | ( cd /path/to/dest/ && tar -xvf - )
  • Т.к. это первый мой топик, буду рад конструктивной критике
Tags:
Hubs:
+66
Comments 67
Comments Comments 67

Articles