Команда cp: правильное копирование папок и файлов в Linux

Компьютерное: Команда cp: правильное копирование папок и файлов в Linux
Img source: vk.com/neoprosveschenie

В этой статье будут раскрыты некоторые неочевидные вещи связанные с использованием wildcards при копировании, неоднозначное поведение команды cp при копировании, а также способы позволяющие корректно копировать огромное количество файлов без пропусков и вылетов.

Допустим нам нужно скопировать всё из папки /source в папку /target.

Первое, что приходит на ум это:
cp /source/* /target

Сразу исправим эту команду на:
cp -a /source/* /target

Ключ -a добавит копирование всех аттрибутов, прав и добавит рекурсию. Когда не требуется точное воспроизведение прав достаточно ключа -r.

После копирования мы обнаружим, что скопировались не все файлы — были проигнорированы файлы начинающиеся с точки типа:
.profile
.local
.mc

и тому подобные.

Почему же так произошло? Потому что wildcards обрабатывает shell (bash в типовом случае). По умолчанию bash проигнорирует все файлы начинающиеся с точек, так как трактует их как скрытые. Чтобы избежать такого поведения нам придётся изменить поведение bash с помощью команды:
shopt -s dotglob

Чтобы это изменение поведения сохранилось после перезагрузки можно сделать файл wildcard.sh c этой командой в папке /etc/profile.d (возможно в вашем дистрибутиве иная папка).

А если в директории-источнике нет файлов, то shell не сможет ничего подставить вместо звёздочки, и также копирование завершится с ошибкой. Против подобной ситуации есть опции failglob и nullglob. Нам потребуется выставить failglob, которая не даст команде выполниться. nullglob не подойдёт, так как она строку с wildcards не нашедшими совпадения в пустую строку (нулевой длины), что для cp вызовет ошибку.

Однако, если в папке тысячи файлов и больше, то от подхода с использованием wildcards стоит отказаться вовсе. Дело в том, что bash разворачивает wildcards в очень длинную командную строку наподобие:
cp -a /souce/a /source/b /source/c …… /target

На длину командной строки есть ограничение, которое мы можем узнать используя команду:
getconf ARG_MAX

Получим максимальную длину командной строки в байтах:
2097152

Или:
xargs --show-limits

Получим что-то типа:
….
Maximum length of command we could actually use: 2089314
….

Итак, давайте будем обходиться вовсе без wildcards. Давайте просто напишем
cp -a /source /target

И тут мы столкнёмся с неоднозначностью поведения cp. Если папки /target не существует, то мы получим то, что нам нужно.

Однако, если папка target существует, то файлы будут скопированы в папку /target/source.

Не всегда мы можем удалить заранее папку /target, так как в ней могут быть нужные нам файлы и наша цель, допустим, дополнить файлы в /target файлами из /source.

Если бы мы папки источника и приёмника назывались одинаково, например, мы копировали бы из /source в /home/source, то мы смогли бы использовать команду:
cp -a /source /home

И после копирования файлы в /home/source оказались бы дополненными файлами из /source.

Такая вот логическая задачка: мы можем дополнить файлы в директории-приёмнике, если папки называются одинаково, но если они отличаются, то папка-исходник будет помещена внутрь приёмника. Как скопировать файлы из /source в /target с помощью cp без wildcards?
Чтобы обойти это вредное ограничение мы используем неочевидное решение:
cp -a /source/. /target

Те кто хорошо знаком с DOS и Linux уже всё поняли: внутри каждой папки есть 2 невидимые папки "." и "..", являющиеся псевдопапками-ссылками на текущую и вышестоящие директории.

  • При копировании cp проверяет существование и пытается создать /target/.
  • Такая директория существует и это есть /target
  • Файлы из /source скопированы в /target корректно.
Итак, вешаем в жирную рамочку в своей памяти или на стене:
cp -a /source/. /target
Поведение этой команды однозначно. Всё отработает без ошибок вне зависимости от того миллион у вас файлов или их нет вовсе.

Выводы
Если нужно скопировать все файлы из одной папки в другую, не используем wildcards, вместо них лучше использовать cp в сочетании с точкой в конце папки-источника. Это скопирует все файлы, включая скрытые и не завалится при миллионах файлов или полном отсутствии файлов.

Послесловие
Аналогичный по результату вариант команды:
cp -a -T /source /target

cp -aT /source /target

ВНИМАНИЕ: регистр буквы T имеет значение. Если перепутать, то получите полную белиберду: направление копирования поменяется.

Источник: Habr.com
комментарий был удален
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.