cho vòng lặp trong các thư mục có ký tự \ n trong tên của chúng


9

Tôi có một số thư mục với \nký tự tên của họ.

ví dụ:

$ ls
''$'\n''Test'

Đó là tham chiếu đến một thư mục có tên Test và một dòng trống trước tên của nó.

Vì vậy, khi tôi chạy một số tập lệnh như thế này, trong thư mục mẹ của nó:

while IFS= read -r d; do 
    rmdir $d
done < <(find * -type d)

Nó cho thấy:

rmdir: failed to remove '': No such file or directory
rmdir: failed to remove 'Test': No such file or directory

Bởi vì nó chạy hai lần, một lần \nvà lần khác Test, vì tên thư mục có hai dòng.

Vậy làm thế nào tôi có thể giải quyết vấn đề này sao cho, script biết \nTestchỉ là một thư mục?


3
Bạn cần sử dụng -print0chỉ thị find và -dtùy chọn đọc. Xem stackoverflow.com/a/40189667/7552
glenn jackman

1
@glennjackman Hãy trả lời!
tráng miệng

@glennjackman Cảm ơn bạn đã trả lời nhưng find * -type d -print0 | while IFS= read -d '' file ; do rmdir $file ; donelệnh có đầu ra này rmdir: failed to remove 'Test': No such file or directory.
Tara S Volpe

5
Bạn phải trích dẫn biến:rmdir "$file"
glenn jackman

1
@glennjackman Chỉ cần đăng bài này là câu trả lời. Đó là một giải pháp thích hợp và bên cạnh những bình luận không phải là nơi tốt nhất cho điều đó. Upvote đã ngụ ý :)
Sergiy Kolodyazhnyy

Câu trả lời:


12

Bạn chỉ có một lệnh duy nhất ở đó, do đó, đủ để gọi findbằng cách -execgọi cờ rmdir:

find -depth -type d -exec rmdir {} \;

Hoặc sử dụng -deletetùy chọn như trong find -type d -delete, nhưng nó sẽ không hoạt động với các thư mục không trống. Cho rằng bạn cũng sẽ cần -emptycờ. Cũng lưu ý, -deletengụ ý -depthđể có thể bỏ qua. Do đó, một sự thay thế khả thi khác giữ mọi thứ như một quá trình:

find -type d -empty -delete

Nếu thư mục không trống, sử dụng rm -rf {} \;. Để tách các thư mục chỉ có \ntên tệp, chúng ta có thể kết hợp trích dẫn ANSI-C của bash $'...'với -nameopption:

find  -type d -name $'*\n*' -empty -delete

POSIX-ly, chúng tôi có thể xử lý theo cách này:

find -depth  -type d -name "$(printf '*\n*' )" -exec rmdir {} \;

Điều đáng nói là nếu mục tiêu của bạn là loại bỏ các thư mục, thì điều đó -deletelà đủ, tuy nhiên nếu bạn muốn thực thi một lệnh trên thư mục thì -execphù hợp nhất.

Xem thêm


2
Sử dụng rm -rf cẩn thận . ; P Không find-deletetùy chọn btw? Nó xóa các thư mục trống và ném lỗi cho những thư mục không trống như rmdir- có thể ít tốn kém hơn.
tráng miệng

3
@dPlay Nó và tôi đã thêm nó vào, nhưng -deletesẽ không xóa các thư mục không trống. Do đó, việc sử dụng cẩn thậnrm -rf
Sergiy Kolodyazhnyy

6
Luôn luôn chạy các findlệnh như vậy mà không có bất kỳ tải trọng phá hủy nào (tức là loại bỏ -deletehoặc -exec whatever \;) để kiểm tra xem danh sách các tệp bị ảnh hưởng có thực sự chính xác không và không chứa nội dung không nên xóa ...
Byte Commander

2
Đây là Askubfox.com để chúng tôi có thể giả sử GNU find. Sử dụng -exec rmdir {} +để bó nhiều đối số vào một rmdirdòng lệnh. Và BTW " nhưng nó sẽ không hoạt động với các thư mục không trống. " Cũng áp dụng cho rmdir, vì vậy đó không thực sự là một sự khác biệt -delete. Đó là một sự khác biệt từ rm -r.
Peter Cordes

2
find -type d -empty -delete( -deletengụ ý -depth).
Kevin

10

Bạn có thể sử dụng các khối vỏ thay vì find:

for d in */ ; do 
    rmdir "$d"
done

Shell global */khớp với tất cả các thư mục trong thư mục hiện tại. Cấu trúc for-loop này sẽ tự động phân tách từ chính xác.

Lưu ý rằng tùy thuộc vào tùy chọn shell của bạn, điều này có thể bỏ qua các thư mục ẩn (tên bắt đầu bằng a.). Hành vi đó có thể được thay đổi để phù hợp với tất cả các tệp cho phiên hiện tại bằng cách sử dụng lệnh shopt -s dotglob.

Cũng đừng quên luôn luôn trích dẫn các biến của bạn.


toàn cầu sẽ chỉ tìm thấy các tệp / thư mục trong thư mục hiện tại, không phải đệ quy như findhiện
ilkkachu

1
Bạn nói đúng @ilkkachu. Điều đó có thể được thay đổi bằng cách kích hoạt tùy chọn shell sao shopt -s globstarvà sử dụng toàn **/*/cầu thay thế.
Chỉ huy Byte

6

Cả hai câu trả lời được viết cho đến nay đều gọi rmdirmột lần cho mỗi thư mục, nhưng rmdircó thể có nhiều đối số tôi tự hỏi: Không có cách nào hiệu quả hơn sao?

Một cách đơn giản có thể làm

rmdir */

và đó chắc chắn là cách dễ nhất và hiệu quả nhất, nhưng nó có thể gây ra lỗi trong trường hợp có nhiều thư mục (xem Độ dài tối đa của các đối số dòng lệnh trong gnome-terminal là bao nhiêu? ). Nếu bạn muốn phương pháp này hoạt động đệ quy, hãy bật globstartùy chọn shell shopt -s globstarvà sử dụng **/*/thay vì */.

Với GNU find(và nếu chúng tôi không chỉ muốn sử dụng -delete), chúng tôi có thể làm

find -depth -type d -exec rmdir {} +

trong đó xargsxây dựng dòng lệnh điều khiển giống như cách xây dựng các dòng lệnh của nó ( man find). Thay thế -depthcho -maxdepth 1nếu bạn không muốn nó hoạt động một cách đệ quy.

Một cách tuyệt vời thứ ba và IMO được giải thích bởi Steeldo trong câu trả lời này :

printf '%s\0' */ | xargs -0 rmdir

Điều này sử dụng shell dựng sẵn printfđể xây dựng một danh sách đối số được phân tách bằng 0, danh sách này sau đó được dẫn đến xargscác cuộc gọi rmdirchính xác thường xuyên khi cần thiết. Bạn có thể làm cho nó hoạt động đệ quy với shopt -s globstar**/*/thay vì */như trên.


2
findlà đệ quy, nhưng vòng lặp của OP thì không. Để nhân rộng hành vi đó, sử dụng find -maxdepth 1 -type d -exec rmdir {} +. ( -depthlà dư thừa trong trường hợp đó.) Thủ thuật gọn gàng printfđể mô phỏng find -print0, tôi đã không thấy điều đó trước đây.
Peter Cordes

1
Oh! Tôi đã thấy lswhilevòng lặp, tôi đã bỏ lỡ rằng họ đang chuyển hướng từ một sự thay thế quá trình thay findvì dẫn lsvào vòng lặp và chỉ không hiển thị nó vì một số lý do. Đó find *thực sự là chôn cất. Có, nó đệ quy và sẽ thất bại nếu có quá nhiều mục nhập thư mục trong thư mục, bao gồm cả không phải thư mục vì nó không sử dụng */. find .sẽ tốt hơn nhiều, vì findnhanh hơn stating so với mở rộng toàn cầu của bash.
Peter Cordes
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.