Bash: Sao chép các tệp được đặt tên một cách đệ quy, bảo toàn cấu trúc thư mục


103

Tôi đã rất hi vọng:

cp -R src/prog.js images/icon.jpg /tmp/package

sẽ mang lại một cấu trúc đối xứng trong dir đích:

/tmp
|
+-- package
    |
    +-- src
    |   |
    |   +-- prog.js
    |
    +-- images
        |
        +-- icon.jpg

nhưng thay vào đó, cả hai tệp đều được sao chép vào / tmp / package. Một bản sao phẳng. (Đây là trên OSX).

Có một hàm bash đơn giản mà tôi có thể sử dụng để sao chép tất cả các tệp, bao gồm các tệp được chỉ định bằng ký tự đại diện (ví dụ: src / *. Js) vào đúng vị trí của chúng trong thư mục đích không. Một chút giống như "cho mỗi tệp, chạy mkdir -p $(dirname "$file"); cp "$file" $(dirname "$file")", nhưng có lẽ là một lệnh duy nhất.

Đây là một chủ đề có liên quan, điều đó cho thấy nó không thể thực hiện được. Mặc dù vậy, giải pháp của tác giả không quá hữu ích đối với tôi, bởi vì tôi chỉ muốn cung cấp danh sách các tệp, ký tự đại diện hoặc không, và tất cả chúng được sao chép vào dir đích. IIRC MS-DOS xcopy thực hiện điều này, nhưng dường như không có tương đương cho cp.

Câu trả lời:


153

Bạn đã thử sử dụng tùy chọn - parent chưa? Tôi không biết OS X có hỗ trợ điều đó không, nhưng nó hoạt động trên Linux.

cp --parents src/prog.js images/icon.jpg /tmp/package

Nếu điều đó không hoạt động trên OS X, hãy thử

rsync -R src/prog.js images/icon.jpg /tmp/package

như aif đề xuất.


4
cảm ơn. hóa ra không thể sử dụng "cp --arent" trên mac, nhưng thật tuyệt khi biết cờ cho unixen khác. rsync -R là giải pháp di động đơn giản nhất cho vấn đề này.
mahemoff

1
Tôi đã chấp nhận cái này vì sự sang trọng / dễ nhớ của nó, nhưng mới phát hiện ra cái này không sao chép toàn bộ thư mục (ít nhất là trên OSX), trong khi cái tar bên dưới thì có.
mahemoff

cp --parentslà tùy chọn bất hợp pháp trong OSX (BSD cp), nhưng gcp(GNU cp) hoạt động tốt. Nếu nó chưa có trong hệ thống của bạn, hãy sử dụng brew install coreutils. U sẽ có nhiều utils với tiền tố g.
kyb

@mahemoff cp -R --parentsrsync -rRsao chép tương đối cả tệp và thư mục.
Vortico

22

Một chiều:

tar cf - <files> | (cd /dest; tar xf -)

ồ, tôi thích điều này hơn nhiều so với câu trả lời của tôi.
EMPraptor

4
Bạn cũng có thể sử dụng -Ctùy chọn để làm chdir cho bạn - tar cf - _files_ | tar -C /dest xf -hoặc một cái gì đó tương tự.
D.Shawley

cảm ơn, điều này là ngắn gọn, mặc dù tôi thích rsync hơn vì sự đơn giản.
mahemoff

1
Tuyệt quá! Có ai biết cách biến điều này thành một lệnh shell không? Nó phải chấp nhận N đầu vào, N-1 đầu tiên là các tệp để sao chép và cuối cùng là thư mục đích.
arod

@arod $ {! #} là tham số cuối cùng và sử dụng tham số này để lấy các đối số trước đó stackoverflow.com/questions/1215538/… . Nếu bạn viết lệnh, vui lòng liên kết đến ý chính tại đây.
mahemoff

18

Ngoài ra, nếu bạn học cũ, hãy sử dụng cpio:

cd /source;
find . -print | cpio -pvdmB /target

Rõ ràng, bạn có thể lọc danh sách tệp theo nội dung trái tim của bạn.

Tùy chọn '-p' dành cho chế độ 'truyền qua' (như đối với '-i' cho đầu vào hoặc '-o' cho đầu ra). '-V' dài dòng (liệt kê các tệp khi chúng được xử lý). '-M' bảo lưu thời gian sửa đổi. '-B' có nghĩa là sử dụng 'khối lớn' (trong đó khối lớn là 5120 byte thay vì 512 byte); có thể nó không có tác dụng trong những ngày này.


1
Tốt hơn là sử dụng -print0với sự kết hợp của --nulltùy chọn để nó không bị vỡ với các ký tự đặc biệt và những thứ như vậy:find . -print0 | cpio -pvdmB --null /target
haridsv

Gọi là old-school, gọi là portable, gọi là gì cũng được, nhưng tôi chắc chắn tin tưởng cpiocho nhiệm vụ này. Tôi đồng ý rằng nên sử dụng các tùy chọn -print0-null, nếu không, tại một số thời điểm, ai đó sẽ cung cấp cho bạn một số thư mục có 'ký tự đặc biệt' (rất có thể là dấu cách) và điều gì đó sẽ xảy ra. Không phải tôi đang nói từ kinh nghiệm cá nhân, nhưng bạn có thể cố gắng sao lưu một loạt các tệp và chỉ kết thúc với một nửa trong số chúng được sao lưu do khoảng trống nằm trong tên tệp. (Được rồi, tôi đang nói từ kinh nghiệm cá nhân.)
bballdave025

@ bballdave025: Bạn chỉ gặp sự cố cpionhư được hiển thị nếu tên tệp chứa dòng mới - sau đó bạn thường nhận được thông báo lỗi về hai (hoặc nhiều) tên tệp không được tìm thấy cho mỗi dòng mới trong tên tệp. (Đôi khi, bạn có thể nhận được ít thông báo hơn - nhưng điều đó đòi hỏi sự cẩn thận đáng kể trong việc xây dựng trường hợp thử nghiệm.). Khi tôi sử dụng cpio, không có --nulltùy chọn; tùy chọn hai dấu gạch ngang không phải là một phần của ký hiệu tùy chọn SVR4 và khái niệm về -print0cũng không có trong findđó. Nhưng đó là một thời gian dài trước đây (ví dụ như giữa những năm 90 trước khi Linux đạt được sự thống trị).
Jonathan Leffler

Cảm ơn vì các chi tiết, @Jonathan_Leffler. Tôi thích học những thứ ở đây. Bây giờ, tôi đang cố gắng nhớ lại lỗi sao chép đệ quy mà tôi đã mắc phải khiến tôi gặp vấn đề với khoảng trắng - có rất nhiều cách tôi có thể mắc phải lỗi đó,
bballdave025

@ bballdave025— Một khả năng đang sử dụng xargstrong hỗn hợp - nó phân tách ở khoảng trắng - khoảng trống, tab, dòng mới. OTOH, tôi không chắc bằng cách nào hoặc tại sao bạn làm điều đó. Bản ghi GNU cpioở chế độ đầu ra rõ ràng về một tên tệp trên mỗi dòng. Sách hướng dẫn sử dụng SVR4 (in năm 1990) khá giả hơn: cpio -o(chế độ sao chép) đọc đầu vào chuẩn để lấy danh sách tên đường dẫn và sao chép các tệp đó vào đầu ra chuẩn cùng với tên đường dẫn và thông tin trạng thái. Do đó, có một cơ hội là nó đã phá vỡ tên tuổi ở các khoảng trắng.
Jonathan Leffler

16

Tùy chọn -R của rsync sẽ thực hiện những gì bạn mong đợi. Đó là một máy photocopy tệp rất giàu tính năng. Ví dụ:

$ rsync -Rv src/prog.js images/icon.jpg /tmp/package/
images/
images/icon.jpg
src/
src/prog.js

sent 197 bytes  received 76 bytes  546.00 bytes/sec
total size is 0  speedup is 0.00

Kết quả mẫu:

$ find /tmp/package
/tmp/package
/tmp/package/images
/tmp/package/images/icon.jpg
/tmp/package/src
/tmp/package/src/prog.js


1

Thử...

for f in src/*.js; do cp $f /tmp/package/$f; done

vì vậy đối với những gì bạn đang làm ban đầu ...

for f in `echo "src/prog.js images/icon.jpg"`; do cp $f /tmp/package/$f; done

hoặc là

v="src/prog.js images/icon.jpg"; for f in $v; do cp $f /tmp/package/$f; done

Bạn không cần echohoặc $vở đây. Ngoài ra phương pháp này sẽ không thành công nếu thư mục tương ứng không tồn tại trong đích.
Weijun Zhou
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.