Команда cp: правильне копіювання папок з файлами *nix

Команда cp: правильне копіювання папок з файлами *nix

У цій статті будуть розкриті деякі неочевидні речі, пов'язані з використанням макетів при копіюванні, неоднозначна поведінка команди 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 з цією командою у папці /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 у поєднанні з точкою в кінці папки-джерела. Це скопіює всі файли, включаючи приховані та не завалиться за мільйонів файлів або повної відсутності файлів.

Післямова

vmspike запропонував аналогічний за результатом варіант команди:

cp -a -T /source /target

Oz_Alex

cp -aT /source /target

УВАГА: регістр літери T має значення. Якщо переплутати, то отримаєте повну безглуздість: напрямок копіювання зміниться.
Подяки:

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

PS Помічені помилки спрямовуйте у особу. Підвищую за це карму.

Команда cp: правильне копіювання папок з файлами *nix

Джерело: habr.com

Додати коментар або відгук