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

в 8:22, , рубрики: linux, UNIX, ответ, метки: ,

Топик написан в ответ на похожий.

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

Но главная проблема этого подхода в другом — он не соответствует идеологии 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/

Задача решена.

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

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

Хочется отметить, что

  • Этот способ далеко не едиственный и даже не самый короткий, а наиболее наглядный для демонстрации методологии решения.
  • Лично я воспользовался-бы tar --exclude=2 -cf - ./ | ( cd /path/to/dest/ && tar -xvf - )
  • Т.к. это первый мой топик, буду рад конструктивной критике

Автор: iamwizard

Поделиться

* - обязательные к заполнению поля