Xóa tất cả các tệp ngoại trừ trong một thư mục con nhất định với find


11

Tôi muốn xóa đệ quy tất cả các tệp không được truy cập trong một thời gian trong thư mục a, ngoại trừ tất cả các tệp trong thư mục con b.

find a \( -name b -prune \) -o -type f -delete

Tuy nhiên, tôi nhận được một thông báo lỗi:

find: Hành động -delete tự động bật -depth, nhưng -prune không làm gì khi -depth có hiệu lực. Nếu bạn vẫn muốn tiếp tục, chỉ cần sử dụng tùy chọn -depth một cách rõ ràng.

Thêm -depthnguyên nhân tất cả các tập tin bđược bao gồm, điều không được xảy ra.

Bất cứ ai cũng biết một cách an toàn để làm cho công việc này?


@ MichaelKjorling: Tôi đã xem qua extglob, nhưng làm thế nào để bạn bao gồm mọi thứ angoại trừ a/b?
forthrin

Không cd a && ls -d !(b/*)làm việc à? (Để làm điều đó, rm -rthay vì ls -d.)
CVn

Đề nghị của bạn xóa các thư mục con. Tôi muốn giữ các thư mục nguyên vẹn. Tôi muốn tìm và xóa tất cả các tệp trong cây bên dưới a(ngoại trừ các tệp bên dưới a/b).
forthrin

Vì vậy, chỉ cần bỏ qua -rđể rm. Có vẻ như những gì bạn đang hỏi về khá dễ dàng được trả lời bằng cách sử dụng tính năng mở rộng của bash, và sau đó những gì bạn làm với kết quả của việc tạo ra là tùy thuộc vào bạn.
một CVn

@ MichaelKjorling Chỉ vì hai vấn đề có các giải pháp tương tự mơ hồ không làm cho các câu hỏi trở thành một bản sao. Hầu hết các giải pháp cho mỗi một trong hai vấn đề không giải quyết được vấn đề khác.
Gilles 'SO- ngừng trở nên xấu xa'

Câu trả lời:


13

TL; DR: cách tốt nhất là sử dụng -exec rmthay vì -delete.

find a \( -name b -prune \) -o -type f -exec rm {} +

Giải trình:

Tại sao tìm thấy phàn nàn khi bạn cố gắng sử dụng -deletevới -prune?

Câu trả lời ngắn: bởi vì -deletengụ ý -depth-depthlàm cho -prunekhông hiệu quả.

Trước khi chúng ta đi đến câu trả lời dài, trước tiên hãy quan sát hành vi tìm và không có -depth:

$ find foo/
foo/
foo/f1
foo/bar
foo/bar/b2
foo/bar/b1
foo/f2

Không có gì đảm bảo về thứ tự trong một thư mục. Nhưng có một đảm bảo rằng một thư mục được xử lý trước nội dung của nó. Lưu ý foo/trước bất kỳ foo/*foo/bartrước bất kỳ foo/bar/*.

Điều này có thể được đảo ngược với -depth.

$ find foo/ -depth
foo/f2
foo/bar/b2
foo/bar/b1
foo/bar
foo/f1
foo/

Lưu ý rằng bây giờ tất cả foo/*xuất hiện trước foo/. Tương tự với foo/bar.

Câu trả lời dài hơn:

  • -prunengăn chặn tìm thấy từ giảm dần vào một thư mục. Nói cách khác -prunebỏ qua nội dung của thư mục. Trong trường hợp của bạn -name b -prunengăn không cho tìm thấy giảm dần vào bất kỳ thư mục có tên b.
  • -depthtìm để xử lý nội dung của một thư mục trước chính thư mục đó. Điều đó có nghĩa là theo thời gian tìm được để xử lý mục nhập thư mục bnội dung của nó đã được xử lý. Do đó -prunelà không hiệu quả với có -depthhiệu lực.
  • -deletengụ ý -depthđể nó có thể xóa các tập tin đầu tiên và sau đó là thư mục trống. -deletetừ chối xóa các thư mục không trống. Tôi đoán có thể thêm một tùy chọn để buộc -deletexóa các thư mục không trống và / hoặc để ngăn chặn -deletengụ ý -depth. Nhưng đó là một câu truyện khác.

Có một cách khác để đạt được những gì bạn muốn:

find a -not -path "*/b*" -type f -delete

Điều này có thể hoặc có thể không dễ nhớ hơn.

Lệnh này vẫn đi vào thư mục bvà xử lý mọi tệp duy nhất trong đó chỉ -notđể từ chối chúng. Đây có thể là một vấn đề hiệu suất nếu thư mục brất lớn.

-pathhoạt động khác hơn -name. -namechỉ khớp với tên (của tệp hoặc thư mục) trong khi -pathkhớp với toàn bộ đường dẫn. Ví dụ quan sát đường dẫn /home/lesmana/foo/bar. -name -barsẽ phù hợp vì tên là bar. -path "*/foo*"sẽ khớp bởi vì chuỗi /foonằm trong đường dẫn. -pathcó một số điều phức tạp bạn nên hiểu trước khi sử dụng nó. Đọc trang người đàn ông findđể biết thêm chi tiết.

Coi chừng rằng điều này không phải là 100%. Có nhiều khả năng "dương tính giả". Cách lệnh được viết ở trên nó sẽ bỏ qua bất kỳ tệp nào có bất kỳ thư mục mẹ nào có tên bắt đầu bằng b(positive). Nhưng nó cũng sẽ bỏ qua bất kỳ tập tin nào tên bắt đầu với bbất kể vị trí trong cây (dương tính giả). Điều này có thể được sửa chữa bằng cách viết một biểu thức tốt hơn "*/b*". Điều đó được để lại như một bài tập cho người đọc.

Tôi giả sử rằng bạn đã sử dụng ablà người giữ chỗ và tên thật giống allosaurusvà hơn brachiosaurus. Nếu bạn brachiosaurusthay thế bthì số lượng dương tính giả sẽ giảm đáng kể.

Ít nhất là các dương tính giả sẽ không bị xóa, vì vậy nó sẽ không bi thảm như vậy. Hơn nữa, bạn có thể kiểm tra dương tính giả bằng cách trước tiên chạy lệnh mà không -delete(nhưng nhớ đặt hàm ý -depth) và kiểm tra đầu ra.

find a -not -path "*/b*" -type f -depth

-not -pathchỉ là điều! Cảm ơn cho một lời giải thích hào phóng!
forthrin

1
Một số chi tiết về lý do tại sao -not -pathhoạt động trong khi -prunekhông hữu ích. Tại sao có thể -not -pathcùng tồn tại với -depth?
Faheem Mitha

3

Chỉ sử dụng rmthay vì -delete:

find a -name b -prune -o -type f -exec rm -f {} +

1
Bạn có thể giải thích lý do tại sao rmlàm việc và deletekhông?
Faheem Mitha

1
Ồ, tôi đoán có lẽ vì "-delete từ chối xóa các thư mục không trống.", Để trích dẫn @lesmana. Vì vậy, từ chối xóa các thư mục không trống. Nhưng rmkhông có vấn đề đó. Nhưng, bất kể, công phu sẽ là một điều tốt.
Faheem Mitha

@FaheemMitha, câu trả lời cho câu hỏi đó. -deletengụ ý -depth, mà rõ ràng là không thể làm việc với -prune. -pathhoạt động, nhưng không dừng lại findtừ việc giảm dần trong các thư mục mà nó không cần khám phá.
Stéphane Chazelas 2/215

0

Các câu trả lời và giải thích ở trên là rất hữu ích.

Tôi sử dụng cách giải quyết của "-exec rm {} +" hoặc "-not -path ... -delete ', nhưng chúng có thể chậm hơn nhiều so với" find ... -delete ". Tôi đã thấy" find ... -delete "chạy 5x nhanh hơn" -exec rm {} + "trên các thư mục sâu trên hệ thống tệp NFS.

Giải pháp '-not path "có chi phí rõ ràng là xem xét tất cả các tệp trong các thư mục bị loại trừ và bên dưới.

Các cuộc gọi "find .. -exec rm {} +" rm mà hệ thống gọi:

fstatat(AT_FDCWD, path...); 
unlinkat(AT_FDCWD, path, 0)

"Find -delete" thực hiện các cuộc gọi hệ thống:

 fd=open(dir,...);
 fchdir(fd); 
 fstatat(AT_FDCWD, filename,...)
 unlinkat(dirfd, filename,...)

Vì vậy, lệnh "-exec rm {} +" rm thực hiện đường dẫn đầy đủ để tra cứu inode hai lần mỗi tệp, nhưng "find -delete" thực hiện một stat và hủy liên kết tên tệp trong thư mục hiện tại. Đó là một chiến thắng lớn khi bạn đang loại bỏ rất nhiều tệp trong một thư mục.

(chế độ rên rỉ trên (xin lỗi))

Có vẻ như thiết kế của sự tương tác giữa -depth, -delete và -prune không cần thiết phải loại bỏ cách thức hiệu quả nhất để thực hiện hành động chung "xóa các tệp ngoại trừ các thư mục trong -prune"

Sự kết hợp của "-type f -delete" sẽ có thể chạy mà không cần -depth vì nó không cố xóa các thư mục. Ngoài ra, nếu "find" có hành động "-deletefile" nói rằng không xóa các thư mục, -depth sẽ không cần phải ngụ ý.

Các lệnh xargs hoặc find -exec tới rm có thể được tăng tốc nếu rm có tùy chọn sắp xếp tên tệp, mở thư mục và thực hiện hủy liên kết (dir_fd, tên tệp) thay vì hủy liên kết các đường dẫn đầy đủ. Nó đã thực hiện unlinkat (dir_fd, tên tệp) khi đệ quy qua các thư mục với tùy chọn -r.

(tắt chế độ rên rỉ)

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.