Cách sao chép một thư mục đệ quy bằng các liên kết cứng cho mỗi tệp


52

Tôi muốn tạo một "bản sao" của cây thư mục trong đó mỗi tệp là một liên kết cứng đến tệp gốc

Ví dụ: Tôi có cấu trúc thư mục:

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

Đây là kết quả mong đợi, một "bản sao" của cây thư mục trong đó mỗi tệp là một liên kết cứng đến tệp gốc:

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

Câu trả lời:


50

Trên Linux (chính xác hơn là với GNU và các busyboxtriển khai cpnhư thường thấy trên các hệ thống có Linux làm kernel) và FreeBSD gần đây, đây là cách:

cp -al dirA dirB

Để biết giải pháp di động hơn, hãy xem câu trả lời bằng pax và cpio của Stéphane Chazelas


Lưu ý rằng pax, như trên FreeBSD, cp -akhông phải là liên kết tượng trưng liên kết cứng.
Stéphane Chazelas

Xin lưu ý rằng các liên kết cứng không hoạt động trên các khung hệ thống tập tin riêng biệt.
Dave

24

POSIXly, bạn sẽ sử dụng paxở chế độ đọc + ghi với -ltùy chọn:

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

( -peLưu giữ tất cả các thuộc tính có thể có của các file (trong trường hợp này chỉ danh bạ) được sao chép, như GNU cp's -alàm).

Bây giờ, mặc dù tiêu chuẩn , lệnh đó không nhất thiết phải rất cơ động .

Đầu tiên, nhiều hệ thống dựa trên GNU / Linux không bao gồm paxtheo mặc định (mặc dù đó là tiện ích POSIX không tùy chọn).

Sau đó, một số lỗi và không phù hợp với một vài triển khai gây ra một số vấn đề với mã đó.

  • do lỗi, Solaris 10 pax(ít nhất) không hoạt động khi sử dụng -rwlkết hợp với -s. Vì một số lý do, có vẻ như nó áp dụng thay thế cho cả đường dẫn gốc và sao chép. Vì vậy, ở trên, nó sẽ cố gắng làm một số link("dirB/file", "dirB/file")thay vì link("dirA/file", "dirB/file").
  • trên FreeBSD, paxkhông tạo liên kết cứng cho các tệp thuộc loại liên kết tượng trưng (hành vi được POSIX cho phép). Không chỉ vậy, nó còn áp dụng thay thế cho các mục tiêu của các liên kết tượng trưng (một hành vi không được POSIX cho phép). Vì vậy, ví dụ nếu có một foo -> AAliên kết tượng trưng trong dirA, nó sẽ trở nên foo -> BAtrong dirB.

Ngoài ra, nếu bạn muốn làm tương tự nhưng với các đường dẫn tệp tùy ý có nội dung được lưu trữ $src$dst, điều quan trọng là phải nhận ra rằng pax -rwl -- "$src" "$dst"tạo cấu trúc thư mục đầy đủ $srcbên trong $dst(phải tồn tại và là một thư mục). Ví dụ, nếu $srcfoo/bar, sau đó, $dst/foo/barđược tạo ra.

Nếu thay vào đó, bạn muốn $dsttrở thành một bản sao của $src, dễ nhất có lẽ là làm như sau:

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

(cũng sẽ giải quyết hầu hết các vấn đề được đề cập ở trên nhưng sẽ thất bại nếu đường dẫn tuyệt đối $dstkết thúc bằng các ký tự dòng mới).

Bây giờ điều đó sẽ không giúp ích cho các hệ thống GNU / Linux khi không có pax.

Thật thú vị khi lưu ý rằng paxđã được POSIX tạo ra để hợp nhất các tính năng của lệnh tarcpiolệnh.

cpiolà một lệnh Unix lịch sử (từ năm 1977) trái ngược với phát minh POSIX và cũng có một triển khai GNU (không phải là paxmột). Vì vậy, mặc dù nó không còn là một lệnh tiêu chuẩn nữa (nó đã có trong SUSv2), nhưng nó vẫn rất phổ biến và có một bộ tính năng cốt lõi mà bạn thường có thể dựa vào.

Tương đương pax -rwlsẽ là cpio -pl. Tuy nhiên:

  1. cpio lấy danh sách tệp đầu vào trên stdin trái ngược với đối số (phân cách dòng mới có nghĩa là tên tệp có ký tự dòng mới không được hỗ trợ)
  2. Tất cả các tệp phải được chỉ định (thông thường bạn cung cấp cho nó đầu ra của find( findcpiođược cùng phát triển bởi cùng một người)).
  3. siêu dữ liệu không được bảo tồn (một số cpiotriển khai có các tùy chọn để bảo tồn một số, nhưng không có gì có thể mang theo được).

Vì vậy, với cpio:

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

Có vẻ như -s / A / B / là cụ thể cho ví dụ của tôi. Bạn sẽ làm điều này như thế nào nếu tên thư mục nguồn và tên thư mục đích là các biến $ sourcedir và $ targetdir?
Gudmundur Orn

@GudmundurOrn, xem chỉnh sửa.
Stéphane Chazelas

Tôi chạy lệnh này trên OS X và chỉ nhận được thông báo lỗi "pax: Không thể liên kết tệp ./a.txt với chính nó". Tôi đã sử dụng lệnh của bạn theo nghĩa đen, chỉ cần thay thế thư mục nguồn bằng tên thực tế, để lại / A / B và dấu chấm cuối cùng. Tôi có hiểu lầm gì không?
db

@db, -s /A/Bthay thế Abằng Bđể dirAtrở thành dirB. Nếu tên thư mục nguồn của bạn không có A, thì nó sẽ sao chép (liên kết) nó qua chính nó. Xem thêm phần còn lại của câu trả lời cho các phương pháp có thể tốt hơn.
Stéphane Chazelas

6

Câu trả lời ngắn:

cd $source_folder
pax -rwlpe . $dest_folder

2

Trong trường hợp bạn đang tìm kiếm tính năng sao chép với liên kết cứng để tạo ảnh chụp nhanh hoặc sao lưu (tất cả hoặc một phần) các tệp của bạn hãy xem rsnapshot.


1
Nó thật thú vị. Nhưng tôi đoán liên kết cứng chỉ là một cơ chế chụp nhanh tốt nếu các tệp sẽ không được sửa đổi. Đúng?
Gudmundur Orn

@Gudmundur Orn; Chính xác. Công cụ được đề cập trong câu trả lời của tôi sẽ tạo ra một ảnh chụp nhanh mới theo cách các tệp là duy nhất; tức là các tệp hiện có (chưa sửa đổi) sẽ được tạo dưới dạng liên kết cứng và các tệp mới (hoặc phiên bản sửa đổi của các tệp hiện có) sẽ được tạo dưới dạng tệp mới. Vì vậy, trong hậu quả, bạn sẽ có ít dự phòng nhất.
Janis

0

Câu trả lời của @ gudmundur-orn là chính xác, nhưng nếu bạn đang sử dụng BtrFS trên Linux cp a --reflink=auto dirA dirBthì nên thực hiện thủ thuật, với sự khác biệt, các tệp thực sự khác nhau và việc thay đổi cái này sẽ không thay đổi cái khác. Bạn có thể đạt được hầu hết tương tự với cp -ctrên máy Mac với APFS ( autosẽ thực hiện sao chép đầy đủ nếu không thể, -csẽ thất bại).

Bất kỳ hệ thống tệp COW nào cũng có thể làm điều đó, nhưng các nhà cung cấp đã không đồng ý về tùy chọn dòng lệnh tiêu chuẩn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.