Ls sẽ luôn liệt kê các tập tin mà rm sẽ loại bỏ?


33

Một cái gì đó tôi cảm thấy tôi nên biết chắc chắn: nếu tôi ls <something>, sẽ rm <something>loại bỏ chính xác các tập tin lsđược hiển thị? Có bất kỳ trường hợp nào rmcó thể loại bỏ các tập tin lskhông hiển thị? (Đây là trong bash 18.04)

Chỉnh sửa: cảm ơn tất cả những người đã trả lời. Tôi nghĩ rằng câu trả lời đầy đủ là sự kết hợp của tất cả các câu trả lời, vì vậy tôi đã chấp nhận câu trả lời được bình chọn nhiều nhất là "câu trả lời".

Những điều bất ngờ tôi đã học được trên đường đi:

  • ls không đơn giản như bạn nghĩ khi xử lý các đối số của nó
  • Trong một cài đặt đơn giản không thay đổi với cài đặt Ubuntu, bí danh .bashrc ls
  • Đừng đặt tên tệp của bạn bắt đầu bằng dấu gạch ngang vì chúng có thể trông giống như đối số lệnh và đặt tên một -r là yêu cầu!

8
Tôi hơi ngạc nhiên khi rmkhông có --dry-runcờ ...
fkraiem

20
@Rinzwind Tại sao sẽ find -deletetốt hơn rm? Bạn nói "Đó là lý do tại sao" , nhưng nó hoàn toàn không rõ ràng với tôi những gì đề cập đến. Cũng lưu ý rằng findlời mời của bạn sẽ xóa tất cả các tệp đệ quy trong thư mục hiện tại, nơi rmsẽ chỉ xóa các tệp trong thư mục ngay lập tức. Cũng -name *là một không-op. Nói chung, tôi khá bối rối trước lời khuyên của bạn ...
marcelm

3
@marcelm Tôi nghĩ lời khuyên cho việc sử dụng findlà vì bạn có thể chạy nó, xem tất cả các tệp và sau đó chạy cùng một lệnh với -delete. Vì bạn đã thấy kết quả từ findđó, nên không có sự mơ hồ về những gì sẽ bị xóa (tôi thực sự muốn nghe thêm chi tiết về điều này dưới dạng câu trả lời)
Scribpetacher

5
@Scrippetacher "... bạn có thể chạy nó, xem tất cả các tệp và sau đó chạy cùng một lệnh với -delete" - Nhưng làm thế nào tốt hơn là chạy ls <filespec>, theo sau rm <filespec>(mà OP đã biết cách thực hiện)?
marcelm

12
@Rinzwind "Điều đó giải quyết câu trả lời của choroba ngay lập tức nơi một tệp được tạo sau ls và trước rm." - Không, không. Nếu bạn chạy find ... -printtrước để xác nhận tệp nào sẽ bị xóa, và sau đó find ... -delete, bạn vẫn sẽ xóa các tệp được tạo giữa hai lệnh. Nếu bạn sử dụng cả hai -print-delete, bạn không nhận được xác nhận, chỉ là một báo cáo thực tế về những gì đã bị xóa (và bạn cũng có thể sử dụng rm -v).
marcelm

Câu trả lời:


40

Vâng, cả hai lsrmhoạt động trên các đối số được truyền cho họ.

Các đối số này có thể là một tệp đơn giản, do đó ls file.ext, rm file.exthoạt động trên cùng một tệp và kết quả là rõ ràng (liệt kê tệp / xóa tệp).

Nếu đối số thay vào đó là một thư ls directorymục , hãy liệt kê nội dung của thư mục trong khi rm directorysẽ không hoạt động (nghĩa là rmkhông có cờ thì không thể xóa thư mục, trong khi nếu bạn làm như vậy rm -r directory, nó sẽ xóa đệ quy tất cả các tệp bên dưới directory và chính thư mục đó ).

Nhưng hãy nhớ rằng các đối số dòng lệnh có thể bị mở rộng shell , vì vậy không phải lúc nào cũng đảm bảo rằng các đối số tương tự được truyền cho cả hai lệnh nếu chúng chứa ký tự đại diện, biến, đầu ra từ các lệnh khác, v.v.

Như một ví dụ cực đoan nghĩ ls $(rand).txtrm $(rand).txt, các đối số là "giống nhau" nhưng kết quả hoàn toàn khác nhau!


3
Ngoài ra, hãy xem xét lsso với rm *nơi có các tệp "ẩn" (dấu chấm), mặc dù đó không phải là một so sánh công bằng như tôi đã không viết ls *. Nhưng rmbản thân nó là vô nghĩa nên toàn bộ điều là táo và cam thực sự. Nếu tôi hiểu chính xác, đó là mấu chốt của câu trả lời của bạn, thì rất tốt :)
Cuộc đua nhẹ nhàng với Monica

5
Một nhận xét khác về lsvs rm -r: lệnh ls <directory>sẽ không hiển thị các tệp ẩn bên trong thư mục, nhưng rm -r <directory> sẽ xóa ngay cả các tệp bị ẩn.
Daniel Wagner

1
@LightnessRacesinOrbit lssẽ không liệt kê chúng (trừ khi có bí danh ls -a) và rm *sẽ không xóa chúng (trừ khi bạn đã dotglobđặt).
Ngừng làm hại Monica

20

Nếu bạn đang nghĩ về một cái gì đó như ls foo*.txtso với rm foo*.txt, thì có, họ sẽ hiển thị và xóa các tệp tương tự. Shell mở rộng toàn cầu và chuyển nó tới lệnh đang được đề cập và các lệnh hoạt động trên các tệp được liệt kê. Một danh sách chúng, một loại bỏ chúng.

Sự khác biệt rõ ràng là nếu bất kỳ tệp nào trong số đó là thư mục, thì lssẽ liệt kê nội dung của nó, nhưng rmsẽ không xóa được. Đó thường không phải là một vấn đề, vì rmsẽ loại bỏ ít hơn những gì được hiển thị bởi ls.

Vấn đề lớn ở đây xuất phát từ việc chạy ls *hoặc rm *trong một thư mục chứa tên tệp bắt đầu bằng dấu gạch ngang . Chúng sẽ mở rộng thành các dòng lệnh của hai chương trình như thể bạn tự viết chúng ra và ls-rnghĩa là "thứ tự sắp xếp ngược", trong khi rmđó có -rnghĩa là loại bỏ đệ quy. Sự khác biệt quan trọng nếu bạn có thư mục con sâu ít nhất hai cấp. ( ls *sẽ hiển thị nội dung của các thư mục cấp đầu tiên, nhưng rm -r *tất cả mọi thứ cũng sẽ vượt qua lớp con đầu tiên.)

Để tránh điều đó, hãy viết các khối lượng cho phép với một đầu ./để chỉ ra thư mục hiện tại và / hoặc đặt một --dấu hiệu để kết thúc quá trình xử lý tùy chọn trước toàn cầu (tức là rm ./*hoặc rm -- *).

Với một thế giới như thế *.txt, đó thực sự không phải là vấn đề vì dấu chấm là ký tự tùy chọn không hợp lệ và sẽ gây ra lỗi (cho đến khi ai đó mở rộng các tiện ích để phát minh ra ý nghĩa của nó), nhưng dù sao vẫn an toàn hơn khi đặt ./nó ở đó.


Tất nhiên, bạn cũng có thể nhận được các kết quả khác nhau cho hai lệnh nếu bạn thay đổi tùy chọn toàn cầu của shell hoặc các tệp được tạo / di chuyển / loại bỏ ở giữa các lệnh, nhưng tôi nghi ngờ bạn có nghĩa là bất kỳ trường hợp nào trong số đó. (Xử lý các tệp mới / đã di chuyển sẽ cực kỳ lộn xộn để thực hiện một cách an toàn.)


1
Cảm ơn bạn. Điều này dường như làm nổi bật một vấn đề với toàn bộ cú pháp dòng lệnh: nếu bạn đặt tên cho các tệp bắt đầu bằng dấu gạch ngang, bạn đang đi trong vùng nước nguy hiểm. Ai biết được những lệnh nào bạn có thể sử dụng trong tương lai, rất lâu sau khi bạn quên mất các tệp -.
B.Tanner

1
@ B.Tanner, vâng. Theo một nghĩa nào đó, vấn đề là các đối số dòng lệnh chỉ là các chuỗi đơn giản. Nếu hệ thống được thiết kế ngày hôm nay, có thể có nhiều cấu trúc hơn trong đó để chương trình được thực thi có thể biết liệu một đối số có được coi là cờ tùy chọn hay không. Ngoài ra còn có các vấn đề khác đến từ cấu trúc rất lỏng lẻo của tên tệp. Có một bài luận rất kỹ lưỡng về điều đó bởi dwheeler , nhưng tôi phải cảnh báo rằng đọc nó sẽ bị tổn thương. (Hoặc từ chi tiết, hoặc từ sự khủng khiếp hoàn toàn của những gì có thể sai.)
ilkkachu

3
Tôi không thể cưỡng lại, bạn đã bán nó cho tôi, tôi không thể đọc nó bây giờ, với những ký ức về thời gian tôi xoay sở để có được một nhân vật trở về xe ngựa ở cuối rất nhiều tên tập tin (cố gắng xem liệu tôi có thể xây dựng một tệp bó Windows cũng có thể chạy như một tập lệnh bash ... Tôi đã không thấy rằng tập tin sắp tới!)
B.Tanner

17

Bỏ qua hành vi vỏ sò, hãy chỉ tập trung vào những gì rmlscó thể tự giải quyết. Ít nhất một trường hợp lssẽ hiển thị những gì rmkhông thể xóa liên quan đến quyền thư mục và trường hợp khác - các thư mục đặc biệt ....

Quyền thư mục

rmlà một hoạt động trên một thư mục, bởi vì bằng cách xóa một tệp, bạn đang thay đổi nội dung thư mục (hoặc nói cách khác là liệt kê các mục nhập thư mục, vì d irectory không có gì khác hơn là một danh sách tên tệp và inodes ). Điều này có nghĩa là bạn cần quyền ghi trên một thư mục. Ngay cả khi bạn là chủ sở hữu của tệp , không có quyền truy cập thư mục, bạn không thể xóa tệp. Điều ngược lại cũng đúng : rmcó thể xóa các tệp có thể thuộc sở hữu của người khác, nếu bạn là chủ sở hữu thư mục.

Vì vậy, rất có thể bạn đã đọc và thực thi các quyền trên một thư mục, điều này sẽ cho phép bạn duyệt qua thư mục và xem nội dung tốt, chẳng hạn ls /bin/echo, nhưng bạn không thể rm /bin/echotrừ khi bạn là chủ sở hữu /bin hoặc nâng cao các đặc quyền của bạn với sudo.

Và bạn sẽ thấy những trường hợp như thế này ở mọi nơi. Đây là một trường hợp như vậy: https://superuser.com/a/331124/418028


Thư mục đặc biệt '.' và '..'

Một trường hợp đặc biệt là ...thư mục. Nếu bạn làm ls .hoặc ls .., nó sẽ vui vẻ hiển thị cho bạn nội dung, nhưng rmchúng không được phép:

$ rm -rf .
rm: refusing to remove '.' or '..' directory: skipping '.'

15

Nếu bạn nhập ls *và sau đó rm *, có thể bạn sẽ xóa nhiều tệp hơn lshiển thị - chúng có thể đã được tạo trong khoảng thời gian nhỏ giữa cuối lsvà bắt đầu rm.


1
Đó là trường hợp có thể xảy ra đối với /tmp, trong đó nhiều ứng dụng có thể tạo các tệp tạm thời, do đó, đó luôn là khả năng trong cả hai lệnh với *. Tuy nhiên, một số ứng dụng cũng làm cho các tệp ẩn danh bằng cách nhập unlink()chúng trong khi giữ cho tệp xử lý mở, vì vậy nó có thể hiển thị ls *nhưng rm *có thể không bắt được.
Sergiy Kolodyazhnyy

1
@OrangeDog Bạn đang thiếu điểm. Chúng ta đang nói về tình trạng chủng tộc
Sergiy Kolodyazhnyy

1
@OrangeDog Tôi sẽ cần xác minh điều này vì tôi đang sử dụng điện thoại ngay bây giờ, nhưng vấn đề vẫn còn tồn tại ngay cả khi *có sự khác biệt giữa nội dung lssẽ hiển thị và nội dung rmhoạt động vì danh sách nội dung thư mục đã thay đổi ở giữa.
Sergiy Kolodyazhnyy

1
Ngay cả khi bạn chỉ thực hiện một lệnh, vẫn có một điều kiện chạy đua giữa việc mở rộng các đối số và sau đó xóa chúng.
Ngừng làm hại Monica

1
@OrangeDog Tôi có thể đồng ý với điều đó. Vì việc mở rộng ký tự đại diện hoạt động trên các tên tệp hiện có, nên, có một điều kiện chạy đua giữa việc mở rộng ký tự đại diện và một lệnh xử lý nó, do đó ls *trên thực tế có thể hiển thị tên tệp đã biến mất.
Sergiy Kolodyazhnyy

9

ls *rm *không chịu trách nhiệm mở rộng toàn cầu - điều đó được thực hiện bởi shell trước khi chuyển nó tới lệnh.

Điều này có nghĩa là bạn có thể sử dụng bất kỳ lệnh nào với filelist mở rộng - vì vậy tôi sẽ sử dụng thứ gì đó ít nhất có thể.

Vì vậy, một cách tốt hơn để làm điều này (hoặc ít nhất, một cách khác) là bỏ qua người trung gian.

echo *sẽ cho bạn thấy chính xác những gì sẽ được chuyển đến rmlệnh của bạn .


2
Cảm ơn bạn, tôi thích ý tưởng sử dụng echo thay vì ls trong kịch bản này.
B.Tanner

3
Hoặc printf "%s\n" *để có được cái nhìn rõ ràng về tên tệp có khoảng trắng. (Hoặc %qthay vào đó để đối phó với các dòng mới và các ký tự điều khiển, với chi phí đầu ra xấu hơn.)
ilkkachu

4

Làm thế nào về:

$ mkdir what
$ cd what
$ mkdir -p huh/uhm ./-r
$ ls *
uhm
$ rm *
$ ls
-r
$ ls -R
.:
-r

./-r:

Về cơ bản, các ký tự đại diện mở rộng sang nội dung bắt đầu bằng -(hoặc nhập thủ công bắt đầu bằng -nhưng có vẻ hơi giống gian lận) có thể được hiểu theo cách khác lsrm.


4

những trường hợp cạnh mà những gì lshiển thị không phải là những gì rmloại bỏ. Một điều khá cực đoan, nhưng may mắn là lành tính là nếu đối số bạn vượt qua là một liên kết tượng trưng đến một thư mục: lssẽ hiển thị cho bạn tất cả các tệp trong thư mục symlinked, trong khi rmsẽ xóa liên kết tượng trưng, ​​để lại thư mục gốc và nội dung của nó không bị ảnh hưởng:

% ln -s $HOME some_link
% ls some_link    # Will display directory contents  
bin    lib    Desktop ...
% rm some_link
% ls $HOME
bin    lib    Desktop ...

1
Huh. ln -s $HOME some_link; ls some_linkđầu ra some_link@cho tôi, nhưng tôi có lsbí danh ls -F. Rõ ràng, -Fthay đổi hành vi để hiển thị liên kết thay vì hủy bỏ nó. Không mong đợi điều đó.
marcelm

Thật! Ngoài ra, ls -lví dụ, nhắm mục tiêu liên kết, không phải đích đến ... phải có cách để tự kiểm tra liên kết.
alexis

1
Thêm tùy chọn cho câu hỏi mở ra quá nhiều khả năng-- câu trả lời của tôi là về hành vi không được sửa đổi của lsrm.
alexis

Nếu bạn nói ls some_link/,  ls -H some_linkhoặc ls -L some_link, nó sẽ liệt kê thư mục được liên kết đến, ngay cả khi bạn thêm -Fhoặc -l. Ngược lại (sắp xếp), -dnói rằng nhìn vào một thư mục hơn là nội dung của nó; so sánh ls -l /tmpls -ld /tmp.
G-Man nói 'Phục hồi Monica'

Chắc chắn, bạn có thể thêm cờ thay đổi hành vi của ls. Về cơ bản, bạn đang cho thấy tại sao việc liệt kê các hành vi với các cờ khác nhau không đáng để bận tâm cho câu hỏi này ...
alexis

4

Nếu bạn chỉ làm lsthay vì ls -a, có rmthể xóa các tệp ẩn mà bạn chưa thấy lsmà không có -a.

Thí dụ :

Theo:

dir_test
├── .test
└── test2

ls dir_test : sẽ chỉ hiển thị test2

ls -A dir_test : sẽ hiển thị test2 + .test

rm -r dir_test : sẽ xóa tất cả (.test + test2)

Tôi hy vọng điều đó sẽ giúp bạn.


bạn có thể cung cấp một ví dụ? Bởi vì thông thường, rm *sẽ không xóa dotfiles. Nếu có, ls *cũng sẽ cho họ thấy.
marcelm

Không, ls *không hiển thị các tập tin ẩn.
DevHugo

Nhưng vâng, có một chút bối rối, tôi đã thêm một số ví dụ.
DevHugo

1
ls -asẽ liệt kê ., .., .testtest2. Bạn có thể muốn thay đổi ví dụ của mình để sử dụng ls -A, trong đó liệt kê mọi thứ trừ ...(nghĩa là chỉ .testtest2).
G-Man nói 'Phục hồi Monica'

3

Đã có nhiều câu trả lời hay, nhưng tôi muốn thêm một số hiểu biết sâu sắc hơn.

Hãy tự hỏi mình câu hỏi: Có bao nhiêu tham số được truyền vào ls, nếu bạn viết

ls *

...? Lưu ý rằng lslệnh không nhận được *tham số dưới dạng nếu có bất kỳ tệp nào *có thể được mở rộng thành. Thay vào đó, shell trước tiên thực hiện việc tạo khối trước khi gọi lệnh, vì vậy lslệnh thực sự nhận được nhiều tham số như có các tệp được khớp bởi toàn cầu. Để triệt tiêu Globing, trích dẫn tham số.

Điều này đúng cho bất kỳ lệnh: echo *vs echo '*'.

Có một kịch bản, gọi nó countparams.shđể kiểm tra hiệu ứng. Nó cho bạn biết có bao nhiêu tham số đã được thông qua và liệt kê chúng.

#!/bin/bash
echo "This script was given $# parameters."
arr=( "$@" )
for ((i=0;i<$#;i++)); do
        echo "Parameter $((i+1)): ${arr[$i]}"
done

Làm cho nó thực thi và chạy ./countparams.sh *. Học hỏi từ đầu ra của nó!


1

Toàn cầu sẽ mở rộng theo cùng một cách cả hai lần, nếu nội dung thư mục giống nhau ở hai thời điểm khác nhau.


Nếu bạn thực sự muốn kiểm tra những gì sẽ được gỡ bỏ, sử dụng rm -i *.txt. Nó sẽ nhắc bạn riêng cho từng tệp trước khi (cố gắng) xóa nó.

Điều này được đảm bảo an toàn trước các điều kiện chủng tộc:
        ls *.txt/ một tệp mới được tạo / rm *.txt
bởi vì bạn được nhắc nhở cho mọi tệp bởi cùng một chương trình đang thực hiện xóa.


Đây là quá cồng kềnh để sử dụng bình thường, và nếu bạn bí danh rmđể rm -i, bạn sẽ thấy mình sử dụng \rmhoặc rm -fkhá thường xuyên. Nhưng điều đáng nói ít nhất là có một giải pháp cho điều kiện cuộc đua. (Nó thậm chí còn có thể di chuyển tới các hệ thống không phải GNU: POSIX rm(1)chỉ định -itùy chọn .)

Một tùy chọn khác sẽ là một mảng bash : to_remove=(*.txt), sau đó yêu cầu người dùng xác nhận (có lẽ sau khi thực hiện ls -ld -- "${to_remove[@]}"), sau đó rm -- "${to_remove[@]}". Vì vậy, việc mở rộng toàn cầu chỉ được thực hiện một lần và danh sách được chuyển qua nguyên văn rm.

Một tùy chọn có thể sử dụng thực tế khác là GNU rm -I( trang man ), sẽ nhắc nếu xóa hơn 4 mục. (Nhưng không hiển thị cho bạn danh sách, chỉ là tổng số.) Tôi sử dụng alias rm='rm -I'trên máy tính để bàn của mình.

Đó là một biện pháp bảo vệ tốt đẹp chống lại sự trở lại của ngón tay mập với mô hình nửa gõ phù hợp với quá nhiều. Nhưng sử dụng lsđầu tiên thường tốt trong thư mục bạn sở hữu hoặc trên hệ thống một người dùng và khi không có các quy trình nền có thể tạo không đồng bộ các tệp mới ở đó. Để bảo vệ chống lại ngón tay mập, đừng gõ rm -rf /foo/bar/baztừ trái sang phải. rm -rf /là vỏ bọc đặc biệt, nhưng rm -rf /usrkhông phải! Để lại -rfphần đó, hoặc bắt đầu với ls, và chỉ thêm rm -rfphần sau khi gõ đường dẫn.

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.