Команда cp: тонкости копирования папок с файлами в Linux

bktznrwjblg2zidiltr86lsc2tm.gif

В этой статье будут раскрыты некоторые неочевидные вещи связанные с использованием 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 не подойдёт, так как она раскрывает в строку нулевой длины, что для cp вызовет ошибку.

Однако, если в папке тысячи файлов и больше, то от подхода с использованием wildcards стоит отказаться вовсе. Дело в том, что bash разворачивает wildcards в очень длинную командную строку наподобие:

cp -a /souce/a /source/b /source/c …… /target


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

getcong ARG_MAX


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

2097152


Или:

xargs --show-limits


Получим что-то типа:

….
Maximum length of command we could actually use: 2089314
….


Итак, давайте будем обходиться вовсе без wildcards.

Давайте просто напишем

cp -a /source /target


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

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

Не всегда мы можем удалить заранее папку /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 в сочетании с точкой в конце папки-источника. Это скопирует все файлы, включая скрытые и не завалится при миллионах файлов или полном отсутствии файлов.Благодарности:

  • Компании RUVDS.COM за поддержку и возможность публикации в своем блоге на Хабре.
  • За изображение TripletConcept. Картинка очень большая и детальная, можно открыть в отдельном окне.


P.S. Замеченные ошибки направляйте в личку. Повышаю за это карму.
1ba550d25e8846ce8805de564da6aa63.png

© Habrahabr.ru