Czy zawsze będzie wyświetlać pliki, które rm usunie?

33

Czuję, że powinienem wiedzieć na pewno: jeśli to zrobię ls <something>, rm <something>usunę dokładnie te same lswyświetlane pliki ? Czy są jakieś okoliczności, w których rmmożna usunąć pliki, które lsnie były wyświetlane? (To jest w bash 18.04)

Edycja: dziękuję wszystkim, którzy odpowiedzieli. Myślę, że pełna odpowiedź jest kombinacją wszystkich odpowiedzi, więc zaakceptowałem najbardziej uprzywilejowaną odpowiedź jako „odpowiedź”.

Nieoczekiwane rzeczy, których nauczyłem się po drodze:

  • ls nie jest tak prosty, jak mogłoby się wydawać, jeśli chodzi o obsługę argumentów
  • W prostej nieskrępowanej instalacji Ubuntu aliasy .bashrc ls
  • Nie nadawaj nazw plikom zaczynającym się od myślnika, ponieważ mogą one wyglądać jak argumenty poleceń, a nazywanie jednego -r wymaga tego!
B.Tanner
źródło
8
Jestem nieco zaskoczony, że rmnie ma --dry-runflagi ...
fkraiem
20
@Rinzwind Dlaczego miałoby find -deletebyć lepsze niż rm? Mówisz „właśnie dlatego” , ale nie jest dla mnie całkowicie jasne, do czego to się odnosi. Zauważ też, że twoje findwywołanie usunie rekursywnie wszystkie pliki w bieżącym katalogu, rma po prostu usunie pliki z katalogu bezpośredniego. To także -name *nie ma możliwości. Podsumowując, jestem dość zaskoczony twoją radą ...
marcelm
3
@marcelm Myślę, że rada findjest taka, że ​​możesz ją uruchomić, zobaczyć wszystkie pliki, a następnie uruchomić to samo polecenie -delete. Ponieważ już widziałeś wyniki find, nie powinno być dwuznaczności co do tego, co zostanie usunięte (tak naprawdę chciałbym usłyszeć więcej szczegółów na ten temat w formie odpowiedzi)
Scribblemacher
5
@Scribblemacher "... możesz go uruchomić, zobaczyć wszystkie pliki, a następnie uruchomić to samo polecenie za pomocą -delete" - Ale jak to jest lepsze niż uruchamianie ls <filespec>, a następnie rm <filespec>(co OP już wie, jak to zrobić)?
marcelm
12
@Rinzwind "To rozwiązuje natychmiastową odpowiedź choroby, gdzie plik jest tworzony po ls i przed rm." - Nie, nie ma. Jeśli uruchomisz find ... -printnajpierw, aby potwierdzić, które pliki zostaną usunięte, a następnie find ... -deletenadal będziesz usuwać pliki utworzone między dwoma poleceniami. Jeśli użyjesz obu z nich, -printa -deletenie otrzymasz potwierdzenia, tylko raport po fakcie o tym, co zostało usunięte (i równie dobrze możesz użyć rm -v).
marcelm

Odpowiedzi:

40

Cóż, zarówno lsi rmdziałać na argumentach, które są przekazywane do nich.

Te argumenty mogą być plik prosty, więc ls file.exti rm file.extdziałają na tym samym pliku, a wynik jest jasne (lista plik / usuń plik).

Jeśli zamiast argumentem jest katalog, ls directoryzawiera wykaz zawartości katalogu, gdy rm directorynie będzie działać jak jest (czyli rmbez flagi nie można usunąć katalogi, a jeśli nie rm -r directory, to rekurencyjnie usuwa wszystkie pliki mocy directory i samego katalogu ).

Należy jednak pamiętać, że argumenty wiersza poleceń mogą być poddawane rozszerzeniu powłoki , więc nie zawsze jest gwarantowane, że te same argumenty są przekazywane do obu poleceń, jeśli zawierają symbole wieloznaczne, zmienne, dane wyjściowe z innych poleceń itp.

Jako skrajny przykład pomyśl ls $(rand).txti rm $(rand).txt, argumenty są „takie same”, ale wyniki są zupełnie inne!

Pan Shunz
źródło
3
Również rozważyć lsvs rm *gdzie istnieją „ukryte” (kropka) plików, choć nawet to nie jest w ogóle porównania godziwej nie pisałem ls *. Ale rmsamo w sobie jest bez znaczenia, więc tak naprawdę to jabłka i pomarańcze. Jeśli dobrze zrozumiałem, to jest sedno twojej odpowiedzi, więc dobra robota :)
Lekkość ściga się z Monicą
5
Kolejny komentarz na lsżywo rm -r: komenda ls <directory>nie pokaże ukryte pliki w katalogu, ale rm -r <directory> będzie usuwać nawet ukryte pliki.
Daniel Wagner,
1
@LightnessRacesinOrbit lsnie wyświetli ich (chyba że jest to alias ls -a) i rm *nie usunie ich (chyba że dotglobustawiłeś).
Stop Harming Monica,
20

Jeśli myślisz o czymś takim jak ls foo*.txtvs. rm foo*.txt, to tak, będą wyświetlać i usuwać te same pliki. Powłoka rozszerza glob i przekazuje go do danego polecenia, a polecenia działają na wymienionych plikach. Jeden je wymienia, jeden je usuwa.

Oczywista różnica polega na tym, że jeśli którykolwiek z tych plików byłby katalogiem, lswyświetlałby jego zawartość, ale rmnie usunąłby go. Zwykle nie stanowi to problemu, ponieważ rmusunąłby mniej niż to, co pokazał ls.

Dużym problemem tutaj jest uruchomienie ls *lub rm *w katalogu zawierającym nazwy plików zaczynające się od myślnika . Oni poszerzyć do linii poleceń tych dwóch programów, jeśli napisał je sam, a lsweźmie -rw znaczeniu „odwrotny porządek”, podczas gdy rmweźmie -roznaczać rekurencyjnego usunięcia. Różnica ma znaczenie, jeśli masz podkatalogi o głębokości co najmniej dwóch poziomów. ( ls *pokaże zawartość katalogów pierwszego poziomu, ale rm -r *wszystko też przekroczy pierwszy poziom ).

Aby tego uniknąć, napisz permisywne globusy ze znakiem wiodącym, ./aby wskazać bieżący katalog i / lub umieść a, --aby zasygnalizować zakończenie przetwarzania opcji przed globem (tj. rm ./*Lub rm -- *).

W przypadku globu *.txtnie jest to w rzeczywistości problemem, ponieważ kropka jest niepoprawnym znakiem opcji i spowoduje błąd (dopóki ktoś nie rozwinie narzędzi, aby wymyślić dla niego znaczenie), ale mimo to bezpieczniej jest go ./tam umieścić .


Oczywiście możesz również uzyskać różne wyniki dla tych dwóch poleceń, jeśli zmienisz opcje globowania powłoki lub utworzysz / przeniesiesz / usunąłeś pliki między poleceniami, ale wątpię, czy miałeś na myśli któryś z tych przypadków. (Radzenie sobie z nowymi / przeniesionymi plikami byłoby wyjątkowo nieuporządkowane.)

ilkkachu
źródło
1
Dziękuję Ci. Wydaje się to uwypuklać problem z całą składnią wiersza poleceń: jeśli nazywasz pliki zaczynające się od myślnika, płyniesz po niebezpiecznych wodach. Kto wie, jakich poleceń możesz użyć w przyszłości, długo po tym, jak zapomnisz o plikach.
B.Tanner,
1
@ B.Tanner, tak. W pewnym sensie problem polega na tym, że argumenty wiersza poleceń są zwykłymi łańcuchami. Jeśli system został zaprojektowany dzisiaj, może być w nim więcej struktury, aby uruchomiony program wiedział, czy argument ma być flagą opcji, czy nie. Istnieją również inne problemy wynikające z bardzo luźnej struktury nazw plików. Jest bardzo dokładny esej na ten temat autorstwa dwheeler , ale muszę ostrzec, że czytanie go zaboli. (Albo z detalu, albo z całkowitej okropności tego, co może pójść nie tak.)
ilkkachu
3
Nie mogę się oprzeć, sprzedałeś mi go, idę go teraz przeczytać, ze wspomnieniami o czasie, kiedy udało mi się uzyskać znak powrotu karetki na końcu wielu nazw plików (próbując sprawdzić, czy mógłbym skonstruować plik wsadowy systemu Windows, które można również uruchomić jako skrypt bash ... nie widziałem, że jeden nadchodzi!)
B.Tanner
17

Pomijając powłoki zachowanie, skupmy się tylko to, co rmi lsmoże zajmować się sobą. Co najmniej jeden przypadek, w którym lspokaże to, czego rmnie można usunąć, obejmuje uprawnienia do katalogu, a drugi - specjalne katalogi .i ...

Uprawnienia do folderów

rmjest operacją na katalogu, ponieważ usuwając plik, zmieniasz zawartość katalogu (lub innymi słowy listę wpisów do katalogu, ponieważ biblioteka jest niczym więcej niż listą nazw plików i i-węzłów ). Oznacza to, że potrzebujesz uprawnień do zapisu w katalogu. Nawet jeśli jesteś właścicielem pliku , bez uprawnień do katalogu nie możesz usunąć plików. Odwrót : rmmożna usunąć pliki, które mogą być posiadane przez innych, jeśli właściciel katalogu.

Więc możesz bardzo dobrze mieć uprawnienia do odczytu i wykonywania katalogu, które pozwolą ci na przykład przeglądać katalog i przeglądać zawartość w porządku ls /bin/echo, ale nie możesz, rm /bin/echochyba że jesteś właścicielem /bin lub podnosić swoje uprawnienia sudo.

Wszędzie zobaczysz takie przypadki. Oto jeden taki przypadek: https://superuser.com/a/331124/418028


Katalogi specjalne ”. i '..'

Innym szczególnym przypadkiem jest .i ..katalogów. Jeśli to zrobisz ls .lub ls .., z przyjemnością pokaże ci zawartość, ale rmich udostępnianie jest niedozwolone:

$ rm -rf .
rm: refusing to remove '.' or '..' directory: skipping '.'
Sergiy Kolodyazhnyy
źródło
15

Jeśli wpiszesz ls *a następnie rm *, możliwe będzie usunięcie więcej plików niż lspokazał - mogą one zostały stworzone w maleńkim przedziale czasowym pomiędzy końcem lsa początkiem rm.

choroba
źródło
1
Jest to prawdopodobny przypadek /tmp, w którym wiele aplikacji może tworzyć pliki tymczasowe, więc zawsze jest to możliwe w obu poleceniach za pomocą *. Jednak niektóre aplikacje sprawiają, że pliki stają się anonimowe, ponieważ unlink()są otwarte, a uchwyt pliku otwarty, więc może się wyświetlać, ls *ale rm *nie może go złapać.
Sergiy Kolodyazhnyy
1
@OrangeDog Brakuje Ci sensu. Mówimy o stanie wyścigu
Sergiy Kolodyazhnyy
1
@OrangeDog Będę musiał to zweryfikować, ponieważ jestem teraz przy telefonie, ale kwestia nadal jest ważna, nawet jeśli *istnieje różnica między tym ls, co by się wyświetlało, a tym, co rmdziała, ponieważ lista zawartości katalogu zmieniła się pomiędzy.
Sergiy Kolodyazhnyy
1
Nawet jeśli wykonasz tylko jedno polecenie, między rozszerzeniem argumentów a ich usunięciem istnieje warunek wyścigu.
Stop Harming Monica,
1
@OrangeDog Mogę się z tym zgodzić. Ponieważ interpretacja symboli wieloznacznych działa na istniejących nazwach plików, tak, istnieje warunek wyścigu między powłoką rozwijającą symbol wieloznaczny, a poleceniem przetwarzającym go, więc ls *w rzeczywistości może pokazać plik, którego już nie ma.
Sergiy Kolodyazhnyy
9

ls *i rm *nie są odpowiedzialni za rozszerzanie globu - robi to powłoka przed przekazaniem jej do polecenia.

Oznacza to, że możesz używać dowolnego polecenia z rozszerzoną listą plików - więc użyłbym czegoś, co robi tak mało, jak to możliwe.

Lepszym sposobem na zrobienie tego (a przynajmniej innym sposobem) jest pominięcie środkowego człowieka.

echo *pokaże ci dokładnie, co zostanie przekazane twojemu rmpoleceniu.

Cień
źródło
2
Dziękuję, podoba mi się pomysł użycia echa zamiast ls w tym scenariuszu.
B.Tanner,
3
Lub, printf "%s\n" *aby uzyskać jednoznaczny widok nazw plików ze spacjami. (Lub %qzamiast tego radzić sobie również z znakami nowej linii i znakami kontrolnymi, kosztem brzydszych wyników.)
ilkkachu
4

Co powiesz na:

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

./-r:

Zasadniczo symbole wieloznaczne rozwijające się do rzeczy zaczynających się od -(lub ręcznie wprowadzanych rzeczy zaczynających się od, -ale które wyglądają trochę bardziej jak oszukiwanie) mogą być interpretowane inaczej przez lsi rm.


źródło
4

Tam przypadki brzegowe gdzie co lspokazuje nie to, co rmusuwa. Raczej skrajny, ale na szczęście łagodny, jeśli przekazany argument jest dowiązaniem symbolicznym do katalogu: lspokaże wszystkie pliki w katalogu z dowiązaniem symbolicznym, a jednocześnie rmusunie dowiązanie symboliczne, pozostawiając oryginalny katalog i jego zawartość nietkniętą:

% ln -s $HOME some_link
% ls some_link    # Will display directory contents  
bin    lib    Desktop ...
% rm some_link
% ls $HOME
bin    lib    Desktop ...
Alexis
źródło
1
Huh ln -s $HOME some_link; ls some_linkwyniki some_link@dla mnie, ale lsalias do ls -F. Najwyraźniej -Fzmienia zachowanie na wyświetlanie linku zamiast dereferencji. Nie spodziewałem się tego.
marcelm
W rzeczy samej! Także ls -lna przykład celuje w link, a nie w miejsce docelowe ... muszą istnieć sposoby na sprawdzenie samego linku.
Alexis
1
Dodanie opcji do pytania otwiera zbyt wiele możliwości - moja odpowiedź dotyczy niezmodyfikowanego zachowania lsi rm.
Alexis
Jeśli powiesz ls some_link/,  ls -H some_linkczy ls -L some_linkto będzie lista połączony do katalogu, nawet jeśli dodać -Flub -l. I odwrotnie (w pewnym sensie) -dmówi , że należy patrzeć na katalog, a nie na jego zawartość; porównaj ls -l /tmpi ls -ld /tmp.
G-Man mówi „Reinstate Monica”
Jasne, możesz dodać flagi, które zmieniają zachowanie ls. Zasadniczo wykazujesz, dlaczego policzenie zachowań z różnymi flagami nie jest warte zawracania sobie głowy tym pytaniem ...
Alexis
4

Jeśli robisz tylko lszamiast ls -a, tak rmmożna usunąć ukryte pliki nie widzieli się lsbez -a.

Przykład:

Według :

dir_test
├── .test
└── test2

ls dir_test : wyświetli tylko test2

ls -A dir_test : wyświetli test2 + .test

rm -r dir_test : usunie wszystkie (.test + test2)

Mam nadzieję, że ci to pomoże.

DevHugo
źródło
Czy możesz podać przykład? Ponieważ normalnie rm *nie usuwa plików kropkowych. Jeśli tak, ls *pokaże je również.
marcelm
Nie, ls *nie wyświetlaj ukrytych plików.
DevHugo,
Ale tak, to jest trochę zdezorientowane, dodałem kilka przykładów.
DevHugo,
1
ls -apokaże ., .., .testi test2. Możesz zmienić przykład użycia ls -A, który zawiera listę wszystkich elementów oprócz . i ..(tj. Tylko .testi test2).
G-Man mówi „Przywróć Monikę”
3

Istnieje już wiele dobrych odpowiedzi, ale chcę dodać głębszy wgląd.

Zadaj sobie pytanie: ile parametrów zostanie przekazanych ls, jeśli napiszesz

ls *

... Zauważ, że lspolecenie nie otrzymuje *parametru jako, jeśli istnieją pliki, które *można rozwinąć. Zamiast tego powłoka najpierw wykonuje globbing przed wywołaniem polecenia, więc lspolecenie faktycznie otrzymuje tyle parametrów, ile jest plików dopasowanych przez globbing. Aby wyłączyć globowanie, podaj parametr.

To jest prawdziwe dla dowolnej komendy: echo *vs echo '*'.

Jest skrypt, wywołaj go, countparams.shaby przetestować efekt. Informuje, ile parametrów przekazał, i podaje je.

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

Spraw, by był wykonywalny i działał ./countparams.sh *. Ucz się z jego wyników!

rexkogitans
źródło
1

Glob rozszerzy się w ten sam sposób za każdym razem, jeśli zawartość katalogu będzie taka sama w tych dwóch różnych czasach.


Jeśli naprawdę chcesz sprawdzić, co zostanie usunięte, użyj rm -i *.txt. Poprosi Cię osobno o każdy plik przed (próbą) usunięcia.

Gwarantuje to bezpieczeństwo przed warunkami wyścigowymi:
        ls *.txt/ tworzony jest nowy plik / rm *.txt
ponieważ monituje o każdy plik ten sam program, który usuwa.


To jest zbyt skomplikowane dla zwykłego użytkowania, a jeśli alias rmdo rm -i, znajdziesz się przy użyciu \rmlub rm -fdość często. Ale warto przynajmniej wspomnieć, że istnieje rozwiązanie stanu wyścigu. (Jest nawet przenośny na systemy inne niż GNU: POSIX rm(1)określa -iopcję ).

Inną opcją może być tablica bash: to_remove=(*.txt)następnie poproś użytkownika o potwierdzenie (być może po wykonaniu ls -ld -- "${to_remove[@]}") rm -- "${to_remove[@]}". Tak więc ekspansja globu odbywa się tylko raz, a lista jest przekazywana dosłownie do rm.

Inną praktycznie użyteczną opcją jest GNU rm -I( strona podręcznika), która monituje o usunięcie więcej niż 4 elementów. (Ale nie pokazuje listy, tylko sumę.) Używam alias rm='rm -I'na pulpicie.

Jest to miłe zabezpieczenie przed powracającym palcami tłuszczu z półtypowym wzorem, który pasuje zbyt wiele. Ale korzystanie z lspierwszego jest ogólnie dobre w katalogu, który posiadasz lub w systemie dla jednego użytkownika, a gdy nie ma procesów w tle, które mogłyby asynchronicznie tworzyć nowe pliki. Aby uchronić się przed palcami tłuszczu, nie pisz rm -rf /foo/bar/bazod lewej do prawej. rm -rf /jest w specjalnej obudowie, ale rm -rf /usrnie jest! Pomiń -rfczęść lub zacznij od lsi dodaj rm -rfczęść dopiero po wpisaniu ścieżki.

Peter Cordes
źródło