Có bất kỳ nhược điểm nào của việc sử dụng rm $ (ls) để xóa các tập tin không?


13

Tôi đã tự hỏi nếu sử dụng rm $(ls)để xóa các tập tin (hoặc rm -r $(ls)để xóa các thư mục là tốt) có an toàn không? Bởi vì trong tất cả các trang web, mọi người đưa ra những cách khác để làm điều này mặc dù lệnh này có vẻ dễ hơn nhiều so với các lệnh khác.


3
Câu trả lời cơ bản, không. Tôi không thể xử lý các ký tự đặc biệt. Tôi có thể viết một câu trả lời một chút để giải thích điều này chi tiết hơn
Sergiy Kolodyazhnyy

5
touch 'foo -r .. bar'; rm $(ls); thư mục cha mẹ của tôi đi đâu? Ngoài ra, loại thay thế nào bạn đang thấy thậm chí còn phức tạp hơn thế này? rm *dễ dàng hơn nhiều để gõ và suy nghĩ, và an toàn hơn (nhưng không hoàn toàn an toàn; xem câu trả lời của Dennis).
Peter Cordes

1
Ngoài các câu trả lời xuất sắc, hãy lưu ý rằng lscó thể khác nhau giữa các lần thực hiện và do đó là không chuẩn. Tùy thuộc vào những gì bạn cần, xem xét các lựa chọn thay thế như findstat. Bạn chỉ nên sử dụng lscho tiêu dùng của con người, không bao giờ được sử dụng bởi các lệnh khác hoặc trong các tập lệnh.
Paddy Landau

Câu trả lời:


7

Điều này dự định để làm gì?

  • ls liệt kê các tập tin trong thư mục hiện tại
  • $(ls)thay thế đầu ra của lsnhững nơi làm đối số chorm
  • Về cơ bản rm $(ls)là nhằm xóa tất cả các tập tin trong thư mục hiện tại

Chuyện gì với bức tranh vậy ?

lskhông thể xử lý đúng các ký tự đặc biệt trong tên tệp. Người dùng Unix thường khuyên nên sử dụng các phương pháp khác nhau . Tôi cũng đã chỉ ra rằng trong một câu hỏi liên quan về việc đếm tên tập tin . Ví dụ:

$ touch file$'\n'name                                                                                                    
$ ls                                                                                                                     
file?name
$ rm $(ls)
rm: cannot remove 'file': No such file or directory
rm: cannot remove 'name': No such file or directory
$ 

Ngoài ra, như được đề cập chính xác trong câu trả lời của Denis, một tên tệp có dấu gạch ngang hàng đầu, có thể được hiểu là đối số rmsau khi thay thế, điều này đánh bại mục đích xóa tên tệp.

Những gì hoạt động

Bạn muốn xóa các tập tin trong thư mục hiện tại. Vì vậy, sử dụng toàn cầu rm *:

$ ls                                                                                                                     
file?name
$ rm $(ls)
rm: cannot remove 'file': No such file or directory
rm: cannot remove 'name': No such file or directory
$ rm *
$ ls
$ 

Bạn có thể sử dụng findlệnh. Công cụ này thường được đề xuất không chỉ cho thư mục hiện tại - nó có thể đi qua đệ quy toàn bộ cây thư mục và hoạt động trên các tệp thông qua-exec . . .{} \;

$ touch "file name"                                
$ find . -maxdepth 1 -mindepth 1                                                                                         
./file name
$ find . -maxdepth 1 -mindepth 1 -exec rm {} \;                                                                          
$ ls
$ 

Python không có vấn đề với các ký tự đặc biệt trong tên tệp, vì vậy chúng tôi cũng có thể sử dụng nó (lưu ý rằng cái này chỉ dành cho các tệp, bạn sẽ cần sử dụng os.rmdir()os.path.isdir()nếu bạn muốn hoạt động trên các thư mục):

python -c 'import os; [ os.remove(i) for i in os.listdir(".") if os.path.isfile(i) ]'

Trong thực tế, lệnh trên có thể được chuyển thành hàm hoặc bí danh ~/.bashrcđể cho ngắn gọn. Ví dụ,

rm_stuff()
{
    # Clears all files in the current working directory
    python -c 'import os; [ os.remove(i) for i in os.listdir(".") if os.path.isfile(i) ]'

}

Phiên bản Perl của điều đó sẽ là

perl -e 'use Cwd;my $d=cwd();opendir(DIR,$d); while ( my $f = readdir(DIR)){ unlink $f;}; closedir(DIR)'

1
"$(ls)"Ví dụ của bạn chỉ hoạt động nếu chỉ có một tệp trong thư mục. Bạn cũng có thể chỉ sử dụng hoàn thành tab để mở rộng tên tệp vì đó là lần hoàn thành duy nhất.
Peter Cordes

@PeterCordes thực sự, vì một lý do kỳ lạ, nó chỉ hoạt động với một tệp. Đó là một lý lẽ nữa để không sử dụng lssau đó :) Tôi sẽ chỉnh sửa nó
Sergiy Kolodyazhnyy

Tôi sẽ không gọi đó là một lý do kỳ quặc: bạn có thể trích dẫn $(ls)để vô hiệu hóa việc chia tách từ hoặc bạn để việc phân tách từ xảy ra (với kết quả thảm hại). Cách duy nhất để vượt qua một danh sách nhiều chuỗi mà không xử lý dữ liệu dưới dạng mã là với các biến mảng hoặc với \0tư cách là dấu phân cách, nhưng chính trình bao không thể làm điều đó. Vẫn IFS=$'\n'là ít nguy hiểm nhất, nhưng không thể phù hợp find -print0 | xargs -0. Hoặc grep -l --null. Hoặc bạn tránh toàn bộ vấn đề với những thứ như find -exec rm {} +. (Lưu ý +để truyền nhiều đối số cho mỗi lần gọi rm; CÁCH hiệu quả hơn).
Peter Cordes

@PeterCordes Yup, hoàn toàn đồng ý ở đó. Nhưng IFS=$'\n'cũng sẽ thất bại trong trường hợp này, vì tôi có dòng mới trong tên tệp, do đó, việc đặt từ sẽ coi nó là hai tên tệp thay vì một. Tuy nhiên, lý do kỳ lạ là với mặc định IFSlà không gian, tab, dòng mới, bản gốc rm "$(ls)"cũng không thành công, nên xử lý như tôi đã nói tên tệp là hai tên riêng biệt, nhưng không được. Thông thường tôi sử dụng findvới -exec, hoặc find . . .-print0 | while IFS= read -d'' FILENAME ; do . . . donecấu trúc để đối phó với tên tệp. Hoặc người ta có thể sử dụng python, tôi đã thêm ví dụ về điều đó.
Sergiy Kolodyazhnyy

"$(ls)"luôn mở rộng thành một đối số vì các trích dẫn bảo vệ sự mở rộng $(ls)từ tách từ. Giống như họ bảo vệ "$foo".
Peter Cordes

26

Không, nó không an toàn và thay thế thường được sử dụng rm * không an toàn hơn nhiều.

nhiều vấn đề với rm $(ls). Vì những người khác đã trình bày trong câu trả lời của họ, đầu ra của lssẽ được phân chia tại các ký tự có trong dấu tách trường bên trong .

Trường hợp tốt nhất, nó chỉ đơn giản là không hoạt động. Trường hợp xấu nhất, bạn dự định chỉ xóa các tệp (chứ không phải thư mục) - hoặc xóa có chọn lọc một số tệp với -i- nhưng có một tệp có tên c -rftrong thư mục hiện tại. Hãy xem điều gì xảy ra.

$ mkdir a
$ touch b
$ touch 'c -rf'
$ rm -i $(ls)
$ ls
c -rf

Lệnh rm -i $(ls)được yêu cầu chỉ xóa các tệp và hỏi trước khi xóa từng tệp, nhưng lệnh cuối cùng được thực thi đã đọc

rm -i a b c -rf

Vì vậy, nó đã làm một cái gì đó khác hoàn toàn.

Lưu ý rằng rm *chỉ tốt hơn một chút. Với cấu trúc thư mục như trước, nó sẽ hoạt động như dự định ở đây, nhưng nếu bạn có một tệp được gọi -rf, bạn vẫn không gặp may.

$ mkdir a
$ touch b
$ touch ./-rf
$ rm -i *
$ ls
-rf

Có một số lựa chọn thay thế tốt hơn. Những cái dễ nhất chỉ liên quan đến rm và continbing.

  • Lệnh

    rm -- *
    

    sẽ hoạt động chính xác như dự định, ở đâu -- báo hiệu rằng mọi thứ sau nó không nên được hiểu là một tùy chọn.

    Đây là một phần của hướng dẫn cú pháp tiện ích POSIX trong hơn hai thập kỷ nay. Nó phổ biến rộng rãi, nhưng bạn không nên mong đợi nó có mặt ở mọi nơi.

  • Lệnh

    rm ./*
    

    làm cho toàn cầu mở rộng khác nhau và do đó không cần sự hỗ trợ từ tiện ích được gọi.

    Ví dụ của tôi ở trên, bạn có thể thấy lệnh cuối cùng sẽ được thực thi bằng cách thêm tiếng vang .

    $ echo rm ./*
    rm ./a ./b ./-rf
    

    Việc hàng đầu ./ngăn rm vô tình đối xử với bất kỳ tên tệp nào như tùy chọn.


1
Điểm rất tốt, tên tệp với - sau khi mở rộng trở thành cờ rm. +1
Sergiy Kolodyazhnyy

1
Thậm chí còn khó chịu hơn touch 'foo -rf .. bar. Tôi không nghĩ kẻ tấn công có thể nhận được bất kỳ cao hơn thư mục mẹ, trừ khi chúng ta có thể tạo ra một dấu tách đường dẫn trong lsđầu ra của.
Peter Cordes

Kẻ tấn công @Peter sẽ phải có quyền ghi để xóa thư mục bằng sáng chế ở nơi đầu tiên, phải không?
Sergiy Kolodyazhnyy

1
@PeterCordes Tôi không chắc liệu tất cả các phiên bản của rm có lỗi này không, nhưng trên Ubuntu, openSUSE và Fedora, nó nói rm: refusing to remove '.' or '..' directory: skipping '..'hoặc một cái gì đó tương tự khi cố gắng xóa thư mục mẹ.
Dennis

@Serg: kẻ tấn công chỉ gửi cho bạn một tệp .zip có tên tệp đó trong đó và cho phép bạn tự bắn vào chân mình bằng cách giải nén nó và sau đó cố gắng xóa nội dung. Hoặc bằng cách tạo tên tệp đó trong / var / tmp hoặc một cái gì đó.
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.