Cara menyalin direktori secara rekursif menggunakan hardlink untuk setiap file

52

Saya ingin membuat "salinan" dari pohon direktori di mana setiap file adalah hardlink ke file asli

Contoh: Saya memiliki struktur direktori:

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

Ini adalah hasil yang diharapkan, sebuah "salinan" dari pohon direktori di mana setiap file adalah hardlink ke file asli:

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
Gudmundur Orn
sumber

Jawaban:

50

Di Linux (lebih tepatnya dengan GNU dan busyboximplementasi cpseperti yang biasanya ditemukan pada sistem yang memiliki Linux sebagai kernel) dan FreeBSD baru-baru ini, ini adalah caranya:

cp -al dirA dirB

Untuk solusi yang lebih portabel, lihat jawaban menggunakan pax dan cpio oleh Stéphane Chazelas

Gudmundur Orn
sumber
Perhatikan bahwa like pax, pada FreeBSD, cp -atidak memiliki tautan tautan keras.
Stéphane Chazelas
Ketahuilah bahwa tautan keras tidak berfungsi di semua sistem file yang terpisah.
Dave
24

POSIXly, Anda akan gunakan paxdalam mode baca + tulis dengan -lopsi:

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

( -peMempertahankan semua atribut kemungkinan file (dalam hal ini hanya direktori) yang disalin, seperti GNU cp's -atidak).

Sekarang, meskipun standar , perintah itu belum tentu sangat portabel .

Pertama, banyak sistem berbasis GNU / Linux tidak termasuk paxsecara default (meskipun itu utilitas POSIX non-opsional).

Kemudian, sejumlah bug dan ketidaksesuaian dengan beberapa implementasi menyebabkan sejumlah masalah dengan kode itu.

  • karena bug, Solaris 10 pax(setidaknya) tidak berfungsi saat digunakan -rwlbersama dengan -s. Untuk beberapa alasan, tampaknya ini menerapkan substitusi ke path asli dan yang disalin. Jadi di atas, ia akan mencoba melakukan beberapa link("dirB/file", "dirB/file")alih-alih link("dirA/file", "dirB/file").
  • di FreeBSD, paxtidak membuat hardlink untuk file bertipe symlink (perilaku yang diizinkan oleh POSIX). Tidak hanya itu, tetapi juga menerapkan substitusi ke target symlinks (perilaku yang tidak diizinkan oleh POSIX). Jadi misalnya jika ada foo -> AAsymlink di dirA, itu akan menjadi foo -> BAdi dirB.

Juga, jika Anda ingin melakukan hal yang sama tetapi dengan jalur file yang sewenang-wenang yang isinya disimpan dalam $srcdan $dst, penting untuk disadari bahwa pax -rwl -- "$src" "$dst"menciptakan struktur direktori penuh $srcdi dalam $dst(yang harus ada dan menjadi direktori). Misalnya, jika $srcini foo/bar, maka, $dst/foo/bardibuat.

Jika sebaliknya, Anda ingin $dstmenjadi salinannya $src, yang termudah adalah melakukannya sebagai:

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

(yang juga akan mengatasi sebagian besar masalah yang disebutkan di atas tetapi akan gagal jika jalur absolut $dstberakhir pada karakter baris baru).

Sekarang itu tidak akan membantu pada sistem GNU / Linux di mana tidak ada pax.

Sangat menarik untuk dicatat bahwa paxdiciptakan oleh POSIX untuk menggabungkan fitur tardan cpioperintah.

cpioadalah perintah Unix historis (dari 1977) yang bertentangan dengan penemuan POSIX, dan ada implementasi GNU juga (bukan paxsatu). Jadi meskipun itu bukan lagi perintah standar (seperti pada SUSv2), itu masih sangat umum, dan ada serangkaian fitur yang biasanya dapat Anda andalkan.

Setara dengan pax -rwlakan cpio -pl. Namun:

  1. cpio mengambil daftar file input pada stdin sebagai lawan dari argumen (baris baru dibatasi yang berarti nama file dengan karakter baris baru tidak didukung)
  2. Semua file harus ditentukan (biasanya Anda memberinya output find( finddan cpiodikembangkan bersama oleh orang yang sama)).
  3. metadata tidak dilestarikan (beberapa cpioimplementasi memiliki opsi untuk mempertahankan sebagian, tetapi tidak ada yang portabel).

Jadi dengan cpio:

absolute_dst=$(umask 077 && mkdir -p -- "$dst" && cd -P -- "$dst" && pwd -P) &&
(cd -P -- "$src" && find . | cpio -pl "$absolute_dst")
Stéphane Chazelas
sumber
Tampaknya -s / A / B / khusus untuk contoh saya. Bagaimana Anda melakukan ini jika nama direktori sumber dan nama direktori target adalah variabel $ sourcedir dan $ targetdir?
Gudmundur Orn
@GudmundurOrn, lihat edit.
Stéphane Chazelas
Saya menjalankan perintah ini pada OS X dan baru saja menerima pesan kesalahan "pax: Tidak dapat menautkan file ./a.txt ke dirinya sendiri". Saya menggunakan perintah Anda secara harfiah, hanya mengganti direktori sumber dengan nama sebenarnya, meninggalkan / A / B dan titik terakhir apa adanya. Apakah saya salah memahami sesuatu?
db
@ db, -s /A/Bganti Adengan Byang dirAjadi dirB. Jika nama direktori sumber Anda tidak A, maka itu akan menyalin (tautan) itu sendiri. Lihat juga sisa jawaban untuk pendekatan yang mungkin lebih baik.
Stéphane Chazelas
6

Jawaban singkat:

cd $source_folder
pax -rwlpe . $dest_folder
lkraider
sumber
2

Jika Anda mencari fitur copy-with-hardlinks untuk membuat snapshot atau cadangan (semua atau sebagian) file Anda lihat rsnapshot.

Janis
sumber
1
Itu menarik. Tapi saya kira hard-link hanya mekanisme snapshot yang baik jika file tidak akan diubah. Baik?
Gudmundur Orn
@Gudmundur Orn; Ini benar. Alat yang disebutkan dalam jawaban saya akan membuat snapshot baru dengan cara file unik; yaitu file yang ada (tidak dimodifikasi) akan dibuat sebagai hardlink dan file baru (atau versi modifikasi dari file yang ada) akan dibuat sebagai file baru. Jadi sebagai konsekuensinya Anda akan memiliki redundansi paling sedikit.
Janis
0

Jawaban @ gudmundur-orn benar, tetapi jika Anda menggunakan BtrFS di Linux, cp a --reflink=auto dirA dirBlakukan triknya, dengan perbedaan file-file tersebut sebenarnya berbeda dan mengubah yang satu tidak mengubah yang lain. Anda dapat mencapai sebagian besar sama dengan cp -cdi Mac dengan APFS ( autoakan melakukan salinan lengkap jika tidak memungkinkan, -cakan gagal).

Setiap sistem file SAP harus dapat melakukan itu, tetapi vendor belum menyetujui opsi baris perintah standar.

kasar
sumber