Як скопіювати каталог рекурсивно за допомогою жорстких посилань для кожного файлу


52

Я хочу створити "копію" дерева каталогів, де кожен файл є твердим посиланням на вихідний файл

Приклад: у мене є структура каталогу:

dirA/
dirA/file1
dirA/x/
dirA/x/file2
dirA/y/
dirA/y/file3

Ось очікуваний результат - "копія" дерева каталогів, де кожен файл є твердим посиланням на вихідний файл:

dirB/            #  normal directory
dirB/file1       #  hardlink to dirA/file1
dirB/x/          #  normal directory
dirB/x/file2     #  hardlink to dirA/x/file2
dirB/y/          #  normal directory
dirB/y/file3     #  hardlink to dirA/y/file3

Відповіді:


50

Що стосується Linux (точніше з GNU та busyboxреалізаціями, cpяк це зазвичай зустрічається у системах, у яких Linux є ядром) та останніх FreeBSD, це:

cp -al dirA dirB

Для більш портативного рішення дивіться відповідь за допомогою pax та cpio від Stéphane Chazelas


Зауважте, що pax, на FreeBSD, cp -aне посилається на жорстке посилання.
Стефан Шазелас

Майте на увазі, що жорсткі посилання не працюють через окремі кріплення файлової системи.
Дейв

24

POSIXly, ви б використовували paxв режимі читання + запису з -lопцією:

pax -rwlpe -s /A/B/ dirA .

( -peЗберігає всі можливі атрибути файлів (в даному випадку тільки каталоги), які копіюються, як GNU cp«s -aробить).

Тепер, хоч і стандартна , ця команда не обов'язково є дуже портативною .

По-перше, багато систем на базі GNU / Linux paxза замовчуванням не включають (навіть якщо це необов'язкова утиліта POSIX).

Тоді, кілька помилок та невідповідностей із кількома реалізаціями спричиняють ряд проблем із цим кодом.

  • через помилку, Solaris 10 pax(принаймні) не працює при використанні -rwlв поєднанні з -s. З якоїсь причини, здається, застосовується підміна як до оригінального, так і до скопійованого шляху. Отже вище, це намагатиметься зробити link("dirB/file", "dirB/file")замість цього link("dirA/file", "dirB/file").
  • у FreeBSD paxне створює жорстких посилань для файлів типу symlink (поведінка, дозволена POSIX). Мало того, але це також застосовує підстановку до цілей символьних посилань (поведінка, яку не дозволяє POSIX). Наприклад, якщо в ньому є foo -> AAсимпосилання dirA, воно стане foo -> BAв dirB.

Крім того, якщо ви хочете зробити те саме, але з довільними шляхами до файлів, вміст яких зберігається $srcі $dst, важливо усвідомити, що pax -rwl -- "$src" "$dst"створюється повна структура каталогу $srcвсередині $dst(що має існувати і бути каталогом). Наприклад, якщо $srcє foo/bar, то $dst/foo/barстворюється.

Якщо замість цього ви хочете $dstбути копією $src, найпростіше, мабуть, зробити це як:

absolute_dst=$(umask 077 && mkdir -p -- "$dst" && cd -P -- "$dst" && pwd -P) &&
(cd -P -- "$src" && pax -rwlpe . "$absolute_dst")

(що також вирішить більшість проблем, згаданих вище, але не вдасться, якщо абсолютний шлях $dstзакінчується символами нового рядка)

Тепер це не допоможе в системах GNU / Linux там, де їх немає pax.

Цікаво зазначити, що paxстворений POSIX для об'єднання функцій команд tarта cpio.

cpio- це історична команда Unix (з 1977 р.) на відміну від винаходу POSIX, а також є реалізація GNU (не paxодна). Тож навіть якщо це вже не стандартна команда (хоча це було в SUSv2), вона все ще дуже поширена, і є основний набір функцій, на який зазвичай можна покластися.

Еквівалент pax -rwlбув би cpio -pl. Однак:

  1. cpio приймає список вхідного файлу на stdin на відміну від аргументів (новий рядок, що означає, що імена файлів із символами нового рядка не підтримуються)
  2. Усі файли повинні бути вказані (як правило, ви подаєте їм вихід find( findі cpioбули розроблені спільно тими самими людьми)).
  3. метадані не зберігаються (у деяких cpioреалізаціях є параметри збереження деяких, але нічого портативного).

Так і з cpio:

absolute_dst=$(umask 077 && mkdir -p -- "$dst" && cd -P -- "$dst" && pwd -P) &&
(cd -P -- "$src" && find . | cpio -pl "$absolute_dst")

Здається, що -s / A / B / є специфічним для мого прикладу. Як би ви це зробили, якби ім'я вихідного каталогу та ім'я цільового каталогу були змінними $ sourcedir та $ targetdir?
Гудмундур Орн

@GudmundurOrn, див. Редагування.
Стефан Шазелас

Я запускаю цю команду в OS X і просто отримує повідомлення про помилку "pax: Не вдається зв'язати файл ./a.txt до себе". Я використав вашу команду буквально, просто замінивши вихідний каталог фактичним іменем, залишивши / A / B і остаточну крапку, як є. Я щось нерозумію?
дб

@db, -s /A/Bзамінює Aз Bтим щоб dirAстає dirB. Якщо імені вашого вихідного каталогу немає A, то воно буде копіювати (посилати) його на себе. Дивіться також решту відповідей щодо можливих кращих підходів.
Стефан Шазелас


2

У випадку, якщо ви шукаєте цю функцію копіювання з жорсткими посиланнями, щоб зробити знімки або резервні копії (цілих або частин) ваших файлів rsnapshot.


1
Це цікаво. Але я думаю, що жорсткі посилання є лише хорошим механізмом знімка, якщо файли не будуть змінені. Правильно?
Гудмундур Орн

@Gudmundur Orn; Це правильно. Інструмент, згаданий у моїй відповіді, створить новий знімок таким чином, щоб файли були унікальними; тобто існуючі (немодифіковані) файли створюватимуться як жорсткі посилання, а нові файли (або модифіковані версії існуючих файлів) створюватимуться як нові файли. Тож у наслідок у вас буде найменше надмірність.
Яніс

0

Відповідь @ gudmundur-orn правильна, але якщо ви перебуваєте на BtrFS в Linux, ви cp a --reflink=auto dirA dirBповинні зробити свою справу, з різницею, що файли насправді різні, і зміна одного не змінює іншого. Ви можете досягти в основному того ж, що і cp -cна Mac з APFS ( autoвиконати повну копію, якщо неможливо, -cне вдасться).

Будь-яка файлова система COW повинна це зробити, але постачальники не погодилися на стандартний варіант командного рядка.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.