Làm thế nào để sao chép một thư mục theo cách đệ quy bằng cách sử dụng cp?


46

Khi tôi sử dụng

cp -R inputFolder outputFolder

kết quả phụ thuộc vào ngữ cảnh :

  1. nếu outputFolderkhông tồn tại, nó sẽ được tạo và đường dẫn thư mục nhân bản sẽ là outputFolder.
  2. nếu outputFoldertồn tại, thì bản sao được tạo sẽ làoutputFolder/inputFolder

Điều này thật kinh khủng , vì tôi muốn tạo một số tập lệnh cài đặt và nếu người dùng chạy nó hai lần do nhầm lẫn, anh ta sẽ outputFoldertạo lần đầu tiên, sau đó vào lần chạy thứ hai, tất cả các thứ sẽ được tạo lại một lần nữa outputFolder/inputFolder.

  • Tôi muốn luôn luôn là hành vi đầu tiên: tạo một bản sao bên cạnh bản gốc (như anh chị em).
  • Tôi muốn sử dụng cpđể có thể mang theo (ví dụ: MINGW không rsyncđược giao hàng)
  • Tôi đã kiểm tra cp -R --parentsnhưng điều này sẽ tạo lại đường dẫn lên cây thư mục (vì vậy bản sao sẽ không outputFoldernhưng some/path/outputFolder)
  • --remove-destinationhoặc --updatetrong trường hợp 2 không thay đổi bất cứ điều gì, mọi thứ vẫn được sao chép vàooutputFolder/inputFolder

Có cách nào để làm điều này mà không cần kiểm tra sự tồn tại của outputFolder(nếu thư mục không tồn tại sau đó ...) hoặc sử dụng rm -rf outputFolderkhông?

Cách UNIX di động, đồng ý để làm điều đó là gì?



2
Một số tùy chọn không phải là POSIX, vì vậy những tùy chọn bạn đã thử không khả dụng. Nếu bạn có thể sống với một chút thiếu tính di động, tại sao không sử dụng rsync?
muru

1
Nếu bạn không cần sử dụng cp, đường ống giữa hai tarlệnh là một cách đáng tin cậy để sao chép cây.
R ..

@R .. bạn có thể cung cấp một ví dụ? @muru Tôi muốn điều này hoạt động trong MINGW chưa rsyncđược giao.
jakub.g

Với GNU tar, tar -C input -cf - . | tar -C output -xf -hoạt động. -Cthay đổi thư mục làm việc, nhưng nó không phải là một tùy chọn tiêu chuẩn, vì vậy nếu không được hỗ trợ, bạn cần chạy hai tars trong các thư mục làm việc phù hợp để bắt đầu, ví dụ: ( cd input && tar -cf - . ) | ( cd output && tar -xf - )Tuy nhiên, nếu bạn đang xử lý Windows, tôi sẽ chỉ sử dụng câu trả lời của roaima.
R ..

Câu trả lời:


54

Sử dụng cái này thay thế:

cp -R inputFolder/. outputFolder

Điều này hoạt động chính xác theo cách mà nó cp -R aaa/bbb ccchoạt động: nếu ccckhông tồn tại thì nó được tạo ra như một bản sao bbbvà nội dung của nó; nhưng nếu cccđã tồn tại thì ccc/bbbđược tạo ra như là bản sao bbbvà nội dung của nó.

Đối với hầu hết mọi trường hợp, bbbđiều này đưa ra hành vi không mong muốn mà bạn đã lưu ý trong Câu hỏi của mình. Tuy nhiên, trong tình huống cụ thể bbbnày ., chỉ là , aaa/bbbthực sự aaa/., đến lượt nó thực sự chỉ là aaabởi một tên khác. Vì vậy, chúng tôi có hai kịch bản:

  1. ccc không tồn tại:

    Lệnh này cp -R aaa/. ccccó nghĩa là "tạo cccvà sao chép nội dung aaa/.vào ccc/., tức là sao chép aaavào ccc.

  2. ccc không tồn tại:

    Lệnh này cp -R aaa/. ccccó nghĩa là "sao chép nội dung của aaa/.vào ccc/., tức là sao chép aaavào ccc.


3
Huh, đó là một cái mới đối với tôi, nhưng nó dường như hoạt động! Nó tài liệu này bất cứ nơi nào?
Ilmari Karonen

1
Tôi đã thử nghiệm inputFolder/.thay vì inputFoldervà thực sự nó hoạt động với tôi trên Windows / Git Bash. (Nó không hoạt động tuy nhiên chỉ với một dấu vết /, tôi cũng cần dấu chấm sau dấu gạch chéo). Tôi đã cho +1 vì nó thú vị, mặc dù có thể hơi khó sử dụng nó trong mã công khai :)
jakub.g

@IlmariJaronen nó không cần phải được ghi lại rõ ràng. Thực hiện theo giải thích và bạn sẽ thấy cách đơn giản là "tuân theo các quy tắc".
roaima

18

Không sao chép thư mục, chỉ sao chép nội dung:

## Create the target directory. The -p suppresses error messages
## if the directory already exists
mkdir -p outputFolder

## Copy the contents recursively, this will not recreate the parent
cp -R inputfolder/* outputfolder/

Bằng cách này, cả hai bạn đều đảm bảo rằng thư mục đích được tạo lần đầu tiên khi tập lệnh chạy và tránh sự cố khi chạy lần thứ hai.

Chris Down chỉ ra rất chính xác rằng trong bash, điều này sẽ bỏ qua các tệp có tên bắt đầu bằng a .. Để tránh điều này, bạn có thể chạy shopt -s dotglobtrước khi chạy lệnh trên.

Cả -pchomkdir-Rchocp được xác định bởi POSIX vì vậy điều này sẽ hoàn toàn di động.


6
Lưu ý: điều này sẽ không sao chép các tập tin bắt đầu bằng .. Trong bash, bạn có thể sử dụng dotglobcho điều đó.
Chris Xuống

2
Lưu ý phương pháp này có khả năng thất bại nếu số lượng mục nhập thư mục trong inputfolder lớn, vì việc mở rộng toàn cầu có thể vượt quá độ dài dòng lệnh tối đa.
Tim Cutts

11

Hãy thử -Ttùy chọn để cp. Điều này tồn tại trong GNU coreutils cpphiên bản 8.22; nó có thể không được di động bên ngoài đó.


1
Vâng, có vẻ như đây là chính xác những gì tôi muốn: cp -T( cp --no-target-directory). unix.stackexchange.com/questions/94831/ Thật không may phiên bản của tôi cpcũ hơn nhiều.
jakub.g

1
+1 Được sử dụng ngay hôm nay. -ulà một lựa chọn tuyệt vời để thêm vào để làm cho nó lười biếng.
PSkocik

4

Bạn cũng có thể sử dụng rsync:

rsync -uav inputFolder/ outputFolder/

(lưu ý dấu gạch chéo, đặc biệt là sau dấu đầu tiên)


0

Theo tôi, không có gì đơn giản và dễ mang theo hơn rm -rf outputFolder. Vì vậy, tôi luôn gắn bó với điều đó. Tôi hiểu rằng câu hỏi của bạn là khác nhau, nhưng tôi nghĩ rằng đây là cách thực hành tốt nhất.


Điều đó thất bại nếu có quyền hoặc quyền sở hữu cụ thể trên mục tiêu cần được giữ. Hoặc nếu đó là một liên kết tượng trưng.
roaima

1
Câu hỏi muốn kết quả cpgiống nhau, cả khi thư mục không tồn tại và khi nó tồn tại. Vậy bạn đang nói đến vấn đề gì?
dashohoxha

Các cplệnh như được đưa ra bởi các OP không giữ quyền (hoặc timestamps).
roaima

0

Bạn có thể sử dụng -ttùy chọn cplệnh như:

cp -R inputFolder -t outputFolder

Bây giờ nếu thư mục đích không tồn tại, nó sẽ báo lỗi:

cp: failed to access ‘outputFolder’: No such file or directory

Lưu ý lệnh trên sẽ sao chép inputFoldercùng với nội dung của nó (và không chỉ nội dung bên trong nó)

nếu bạn chỉ muốn sao chép nội dung của inputFoldernó thì hơi khó (vì bạn phải cẩn thận trong khi sử dụng shell shellbing trong khi sử dụng dấu hoa thị *)

cp -R  -t outputFolder/ -- inputFolder/*

Bây giờ nếu thư mục đích không tồn tại, nó sẽ báo lỗi:

cp: failed to access ‘outputFolder’: No such file or directory

làm việc với cp (GNU coreutils) 8.23

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.