TL; DR: najlepszym sposobem jest użycie -exec rm
zamiast -delete
.
find a \( -name b -prune \) -o -type f -exec rm {} +
Wyjaśnienie:
Dlaczego znaleźć narzekać podczas próby korzystania -delete
z -prune
?
Krótka odpowiedź: ponieważ -delete
implikuje -depth
i -depth
czyni -prune
nieskutecznym.
Zanim przejdziemy do długiej odpowiedzi, najpierw obserwuj zachowanie find zi bez -depth
:
$ find foo/
foo/
foo/f1
foo/bar
foo/bar/b2
foo/bar/b1
foo/f2
Nie ma gwarancji na zamówienie w jednym katalogu. Istnieje jednak gwarancja, że katalog zostanie przetworzony przed zawartością. Uwaga foo/
przed jakimkolwiek foo/*
i foo/bar
przed jakimkolwiek foo/bar/*
.
Można to odwrócić za pomocą -depth
.
$ find foo/ -depth
foo/f2
foo/bar/b2
foo/bar/b1
foo/bar
foo/f1
foo/
Zauważ, że teraz wszystkie foo/*
pojawiają się wcześniej foo/
. To samo z foo/bar
.
Dłuższa odpowiedź:
-prune
uniemożliwia zejście find do katalogu. Innymi słowy, -prune
pomija zawartość katalogu. W twoim przypadku -name b -prune
uniemożliwia znalezienie zejście do dowolnego katalogu o nazwie b
.
-depth
wyszukuje, aby przetworzyć zawartość katalogu przed samym katalogiem. Oznacza to, że do czasu, aż find przetworzy pozycję katalogu, b
jej zawartość została już przetworzona. W związku z tym -prune
jest nieskuteczny -depth
.
-delete
oznacza -depth
, że może najpierw usunąć pliki, a następnie pusty katalog. -delete
odmawia usunięcia niepustych katalogów. Myślę, że byłoby możliwe dodanie opcji wymuszenia -delete
usunięcia niepustych katalogów i / lub zapobieżenia -delete
sugerowaniu -depth
. Ale to inna historia.
Istnieje inny sposób na osiągnięcie tego, co chcesz:
find a -not -path "*/b*" -type f -delete
To może być łatwiejsze do zapamiętania.
To polecenie wciąż schodzi do katalogu b
i przetwarza każdy pojedynczy plik tylko po to, -not
aby je odrzucić. Może to być problem z wydajnością, jeśli katalog b
jest ogromny.
-path
działa inaczej niż -name
. -name
pasuje tylko do nazwy (pliku lub katalogu), podczas gdy -path
pasuje do całej ścieżki. Na przykład obserwuj ścieżkę /home/lesmana/foo/bar
. -name -bar
będzie pasować, ponieważ nazwa to bar
. -path "*/foo*"
będzie pasować, ponieważ ciąg /foo
znajduje się na ścieżce. -path
ma pewne zawiłości, które powinieneś zrozumieć przed użyciem. Przeczytaj stronę podręcznika, find
aby uzyskać więcej informacji.
Uważaj, że nie jest to w 100% niezawodne. Istnieją szanse na „fałszywe alarmy”. Sposób, w jaki powyższe polecenie jest napisane, pomija każdy plik, który ma dowolny katalog nadrzędny, którego nazwa zaczyna się od b
(dodatnia). Ale pominie także każdy plik, którego nazwa zaczyna się b
niezależnie od pozycji w drzewie (fałszywie dodatni). Można to naprawić, pisząc lepsze wyrażenie niż "*/b*"
. Zostało to ćwiczeniem dla czytelnika.
Zakładam, że użyłeś a
i b
jako symbole zastępcze, a prawdziwe nazwiska są bardziej podobne do allosaurus
i brachiosaurus
. Jeśli wstawisz brachiosaurus
w miejsce b
wówczas ilość fałszywych alarmów zostanie drastycznie zmniejszona.
Przynajmniej fałszywe pozytywy będzie nie usunięte, więc będzie nie być tak tragiczne. Co więcej, możesz sprawdzić fałszywe alarmy, uruchamiając najpierw komendę bez -delete
(ale pamiętaj, aby umieścić domyślną -depth
) i sprawdź dane wyjściowe.
find a -not -path "*/b*" -type f -depth
a
poza wyjątkiema/b
?