- PVSM.RU - https://www.pvsm.ru -

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

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

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

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

Этот список файлов нужно отфильтровать. Перенаправим [2] вывод нашей предыдущей комманды в программу grep.

$ find ./ -type 'f' | grep 2
./dir2
./file2.txt

Хорошо, но в условиях задачи стоит исключать файлы, так что немного поменеяем наш конвейер [3]

$ find ./ -type 'f' | grep -v 2
./dir1/file7.txt
./file1.bin

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

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

Из man-страницы [4] для команды cp мы можем узнать, что исходный файл нужно передавать программе cp в качестве аргумента, а мы пока можем только перенаправить список на стандартный ввод [5].
Применим утилиту 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


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/linux/14413

Ссылки в тексте:

[1] похожий: http://habrahabr.ru/post/150813/

[2] Перенаправим: http://ru.wikipedia.org/wiki/%D0%9F%D0%B5%D1%80%D0%B5%D0%BD%D0%B0%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_(UNIX)

[3] конвейер: http://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%BD%D0%B2%D0%B5%D0%B9%D0%B5%D1%80_(UNIX)

[4] man-страницы: http://ru.wikipedia.org/wiki/Man

[5] стандартный ввод: http://ru.wikipedia.org/wiki/%D0%A1%D1%82%D0%B0%D0%BD%D0%B4%D0%B0%D1%80%D1%82%D0%BD%D1%8B%D0%B9_%D0%B2%D1%8B%D0%B2%D0%BE%D0%B4