Unix xóa với find: Xóa tất cả các tệp được liệt kê trong một tệp


1

Trong thư mục gốc của tôi, tôi có một vài thư mục có tên giống như AA, BB, CC, vv, mỗi file có chứa trong các định dạng AA1001.txt, BB1002.txtvv Trong thư mục gốc của tôi, tôi cũng có một tập tin all_to_deletetrong đó có một loạt các tên tập tin tách với dòng mới, do đó tìm kiếm đại loại như thế này:

AA1004.txt

BB3004.txt

BB3005.txt ...

Bây giờ tôi muốn đi qua tất cả các thư mục con trong thư mục gốc của mình và xóa tất cả các tệp khớp với tên tệp đã cho. Cho đến bây giờ, tôi đã thử một cái gì đó như:

while read line; do find . -type f -name $line -exec rm -f {} \;; done Mặc dù vậy, điều này không thể hoạt động vì đã while read line; do find . -type f -name $line; donekhông khớp với bất kỳ tệp nào (vì công cụ tìm kiếm cho kết quả đầu ra là ./AA/AA1001.txt...)

Các bạn có một giải pháp cho tôi?

Câu trả lời:


1

Đưa ra một tệp có tên tệp, cách dễ nhất là đọc từng dòng một và chuyển nó đến find. Tuy nhiên, điều này sẽ dẫn đến một thể hiện riêng findcho từng tên tệp và có thể trở nên rất chậm đối với danh sách lớn các tệp và nhiều tệp trong cây thư mục.

Thay vào đó, tôi sẽ làm một cái gì đó như thế này:

find . -type f -name "*txt" | grep -wFf to_delete.txt | xargs -I{} rm '{}'

Mẹo nhỏ là cung cấp cho greptệp của bạn dưới dạng danh sách các mẫu để tìm kiếm ( -f). Các -Fđảm bảo tên tập tin của bạn được coi là chuỗi và biểu thức không thường xuyên, như vậy tên tập tin của bạn ca chứa các ký tự đặc biệt như *hoặc [hoặc |. Sau đó, bạn chuyển đến xargsvà sử dụng trích dẫn '{}' , nếu không, nó bị lỗi trên khoảng trắng, để xóa các tệp.

LƯU Ý : Điều này giả định rằng tên tệp của bạn là duy nhất, một tên có thể được chứa trong tên khác. Ví dụ: bạn không có tệp được gọi foofoobar. Nếu bạn làm, đưa ra một mẫu foo, điều này sẽ xóa cả hai tập tin. Để tránh việc sử dụng này:

while IFS= read -r line; do find . -name "$line" -delete; done < to_delete.txt 

Từ man find:

   -delete
          Delete files; true if removal succeeded. 

Tôi đã có một nhận xét về tên tập tin chồng chéo tất cả được thiết lập, khi tôi thấy chỉnh sửa của bạn. Như với tất cả các giải pháp khác bằng cách sử dụng findlệnh, chương trình này sẽ thất bại nếu có của tên tập tin (đặc biệt là bất kỳ của các dòng trong to_delete.txt) chứa một ký tự đại diện: *, ?, hoặc [... ].
Scott

@Scott hoàn toàn đúng về sự chồng chéo, tôi nhận thấy ngay sau khi đăng nên đã xóa câu trả lời trong khi tôi đang chỉnh sửa. Đối với các ký tự đại diện, cả hai giải pháp đều xử lý chúng một cách chính xác (tôi đã thử nghiệm *). Có lẽ bạn đã bỏ lỡ Ftùy chọn grep. Tôi sẽ chỉnh sửa và làm cho nó rõ ràng hơn. Cảm ơn vì đã chỉnh sửa, nchìa khóa của tôi có vấn đề.
terdon

Đúng là tôi đã che đậy -F, và, khi nhìn lại, tất cả các giải pháp khác sử dụng findlệnh LỚN là quá rộng, nhưng có lẽ bạn đã bỏ lỡ quan điểm của tôi. Trong trường hợp bệnh lý mà bạn có một tệp có tên *, điều gì xảy ra nếu bạn làm điều đó find . –name "*" …?
Scott

@ Hủy bỏ ah, trong trường hợp đó, giải pháp đầu tiên của tôi hoạt động tốt, lần thứ hai của tôi thất bại và xóa mọi thứ. Đủ công bằng :).
terdon

1

Làm thế nào nghĩa đen là mô tả của bạn về vấn đề của bạn? Là tên thư mục tất cả hai nhân vật? Tập tin sẽ BB3004.txtluôn ở trong thư mục BB? Nếu có, thì bạn không cần find; chỉ cần trích xuất tên thư mục từ hai ký tự đầu tiên của tên tệp:

while read -r line
do
    dir=$(expr "$line" : '\(..\)')
    echo rm "$dir/$line"
    rm "$dir/$line"
done < all_to_delete

+1. Tôi đã thêm vào -rđể cho nó xử lý dấu gạch chéo ngược nhưng bạn có nhận ra rằng nếu có một tệp được gọi là *nó sẽ được mở rộng sang /*phải không? Nếu bạn may mắn, bạn sẽ nhận được thông báo lỗi :).
terdon

Vâng, họ hoàn toàn theo nghĩa đen. Thật tuyệt, tôi không biết về kiểu khai thác chuỗi con này!
conipo

Chà, tôi đoán đến lượt tôi cảm ơn bạn đã chỉnh sửa. Tôi mất vài phút để hiểu ý của bạn; cụ thể là, nếu dòng Line (tên tệp) chỉ dài một ký tự, thì dir dir dir được đặt thành null (vì exprkhông thành công, vì không có hai ký tự đầu tiên). OK, đúng, đó sẽ là một điều tốt để kiểm tra. Nhưng lưu ý rằng tôi đang làm việc theo giả định rằng, như trong các ví dụ trong câu hỏi, tất cả các tên tệp dài ít nhất hai ký tự. Và ngay cả khi có *được trong đó, tất cả những gì nó sẽ làm là cố gắng xóa một tệp được gọi /*- vì nó được trích dẫn, nó sẽ không được mở rộng.
Scott

Bây giờ một cái gì đó tôi vừa khám phá và không hiểu 100% là exprthất bại (với thông báo lỗi) nếu đối số đầu tiên là --(hai dấu trừ). Nhưng tôi biết làm thế nào để khắc phục điều đó : expr X"$x" : X'\(..\)'.
Scott

1

Có nhiều cách khác nhau để thực hiện điều đó, nhưng đây là cách tôi thực hiện:

Tạo một thư mục thử nghiệm với một vài tệp trong đó:

% ls ./teste
lala  lele  lolo  lulu

Tạo một tệp liệt kê những cái tôi muốn xóa:

% cat to_delete.txt 
lele
lolo
lulu

Ở đây tôi lặp máng từng dòng của tệp 'to_delete.txt' chuyển tên tệp đến lệnh find và sau đó xóa chúng:

% while read filename; do find ./teste -name ${filename} -print0 | xargs -0 rm -vf; done < to_delete.txt 
removed `./teste/lele'
removed `./teste/lolo'
removed `./teste/lulu'

Làm xong:

% ls ./teste                                                                                      
lala

Đẹp, nhưng không sử dụng forcác vòng lặp cho tên tệp. Họ thất bại nếu tên chứa một khoảng trắng. Vòng lặp while là giải pháp chính xác.
terdon

@terdon, bạn đúng rồi. Vòng lặp for trở nên vô nghĩa khi có khoảng trắng trong tên tệp đích. Hoàn toàn bất ngờ! Tôi đã loại bỏ nó để ủng hộ việc 'mong manh' trong khi đọc.
Luis Nardella

À , bây giờ tôi có thể nâng cao ý thức với một lương tâm rõ ràng :)
terdon

{}tên tập tin xung quanh cần thiết? Điều này làm việc như là tốt. Sự khác biệt mặc dù khi sử dụng là gì -print0xargs -0như trái ngược với lại -print0-0ra?
conipo

@Jonathan: Dấu ngoặc nhọn {} không cần thiết trong ví dụ này, tuy nhiên tôi cho rằng luôn luôn sử dụng nó để thực hành mã hóa tốt. Nếu bạn để print0 và -0 out, lệnh rm sẽ thất bại nếu tên tệp chứa khoảng trắng hoặc các ký tự đặc biệt khác. Bạn có thể tìm hiểu thêm về các tùy chọn đó tại các trang man tương ứng của họ [123]. [1] man7.org/linux/man-pages/man1/bash.1.html#EXPANSION - [2] man7.org/linux/man-pages/man1/find.1.html#EXPRESSIONS - [3] man7 .org / linux / man-page / man1 / xargs.1.html # TÙY CHỌN
Luis Nardella
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.