Chcę rekurencyjnie usunąć wszystkie pliki, do których nie ma dostępu w danym a
momencie w folderze , z wyjątkiem wszystkich plików w podfolderze b
.
find a \( -name b -prune \) -o -type f -delete
Jednak pojawia się komunikat o błędzie:
find: Akcja -delete automatycznie włącza -depth, ale -prune nic nie robi, gdy działa -depth. Jeśli mimo to chcesz kontynuować, po prostu jawnie użyj opcji -depth.
Dodanie -depth
powoduje b
włączenie wszystkich plików , co nie może się zdarzyć.
Czy ktoś zna bezpieczny sposób, aby to zadziałało?
a
poza wyjątkiema/b
?cd a && ls -d !(b/*)
zadziała? (Aby to zrobić,rm -r
zamiast tegols -d
.)a
(oprócz plików poniżeja/b
).-r
do rm. Wygląda na to, że na to, o co pytasz, można dość łatwo odpowiedzieć za pomocą rozszerzonego globowania bash, a następnie to, co zrobisz z wynikiem globowania, zależy od ciebie.Odpowiedzi:
TL; DR: najlepszym sposobem jest użycie
-exec rm
zamiast-delete
.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
:Nie ma gwarancji na zamówienie w jednym katalogu. Istnieje jednak gwarancja, że katalog zostanie przetworzony przed zawartością. Uwaga
foo/
przed jakimkolwiekfoo/*
ifoo/bar
przed jakimkolwiekfoo/bar/*
.Można to odwrócić za pomocą
-depth
.Zauważ, że teraz wszystkie
foo/*
pojawiają się wcześniejfoo/
. To samo zfoo/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 nazwieb
.-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:
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 katalogb
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 tobar
.-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
ib
jako symbole zastępcze, a prawdziwe nazwiska są bardziej podobne doallosaurus
ibrachiosaurus
. Jeśli wstawiszbrachiosaurus
w miejsceb
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.źródło
-not -path
było po prostu rzeczą! Dzięki za hojne wyjaśnienie!-not -path
działa, podczas gdy-prune
nie. Dlaczego może-not -path
współistnieć z-depth
?Po prostu użyj
rm
zamiast-delete
:źródło
rm
działa, a dlaczegodelete
nie?rm
nie ma tego problemu. Ale niezależnie od tego, opracowanie byłoby dobre.-delete
implikuje-depth
, co oczywiście nie działa-prune
.-path
działa, ale nie przestajefind
schodzić do katalogów, których nie musi eksplorować.Powyższe odpowiedzi i wyjaśnienia były bardzo pomocne.
Używam obejść „-exec rm {} +” lub „-not -path ... -delete”, ale mogą one być znacznie wolniejsze niż „find ... -delete”. Widziałem „find ... -delete ”działa 5 razy szybciej niż„ -exec rm {} + ”w głębokich katalogach w systemie plików NFS.
Rozwiązanie „-not path” ma oczywisty narzut związany z przeglądaniem wszystkich plików w wykluczonych katalogach i poniżej.
„Find .. -exec rm {} +” wywołuje rm, który wykonuje wywołania systemowe:
Funkcja „find -delete” wykonuje wywołania systemowe:
Zatem polecenie „-exec rm {} +” rm wykonuje pełną ścieżkę do wyszukiwania i-węzłów dwa razy dla każdego pliku, ale polecenie „find -delete” wykonuje stat i rozłącza nazwę pliku w bieżącym katalogu. To duża wygrana, gdy usuwasz wiele plików w jednym katalogu.
(tryb kwilenia włączony (przepraszam))
Wygląda na to, że projekt interakcji między -depth, -delete i -prune niepotrzebnie eliminuje najbardziej efektywny sposób wykonywania wspólnego działania „usuń pliki z wyjątkiem tych katalogów w -prune”
Kombinacja „-type f -delete” powinna być w stanie działać bez -depth, ponieważ nie próbuje usuwać katalogów. Alternatywnie, jeśli polecenie „znajdź” zawierało akcję „-deletefile”, która mówi „nie usuwaj katalogów”, -depth nie musiałby być implikowany.
Polecenia xargs lub find -exec do rm mogłyby zostać przyspieszone, jeśli rm miałby opcję sortowania nazw plików, otwierania katalogów i wykonywania unlinkat (dir_fd, nazwa pliku) zamiast rozłączania pełnych ścieżek. Już robi unlinkat (dir_fd, nazwa_pliku) podczas rekurencji przez katalogi z opcją -r.
(wyłączony tryb kwilenia)
źródło