MyTetra Share
Делитесь знаниями!
Копирование файлов и каталогов в Bash командой cp - разбор неочевидных моментов
Время создания: 17.02.2021 09:25
Автор: Сергей Каменев
Текстовые метки: linux, bash, командная строка, копирование, cp, файл, каталог, опции, слеш, косая, путь, команда
Раздел: Компьютер - Linux - Bash - Команды и скрипты
Запись: xintrea/mytetra_syncro/master/base/1613543155425l43vmue/text.html на raw.github.com

Допустим, нужно скопировать всё из папки /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 имеет значение, так как у команды cp есть опция -t, которая используется для указания директории назначения при применении команды cp в совокупности с другими консольными командами.


Так же в этом разделе:
 
MyTetra Share v.0.59
Яндекс индекс цитирования