Usuń wszystkie pliki oprócz plików z rozszerzeniem pdf z katalogu

50

Mam katalog, który zawiera następujące elementy:

x.pdf
y.zip
z.mp3
a.pdf

Chcę usunąć wszystkie pliki oprócz x.pdfi a.pdf. Jak to zrobić z terminala? Nie ma podkatalogów, więc nie ma potrzeby rekurencji.

Starkers
źródło

Odpowiedzi:

63
cd <the directory you want>
find . -type f ! -iname "*.pdf" -delete
  • Pierwsze polecenie przeniesie Cię do katalogu, w którym chcesz usunąć swoje pliki
  • Drugie polecenie usunie wszystkie pliki oprócz tych, które kończą się na .pdfw nazwie pliku

Na przykład, jeśli tempw twoim katalogu domowym znajduje się katalog:

cd ~/temp

następnie usuń pliki:

find . -type f ! -iname "*.pdf" -delete

Spowoduje to usunięcie wszystkich plików oprócz xyz.pdf.

Możesz połączyć te dwa polecenia, aby:

find ~/temp -type f ! -iname "*.pdf" -delete

.jest bieżącym katalogiem. !oznacza zabranie wszystkich plików oprócz tych .pdfna końcu. -type fwybiera tylko pliki, a nie katalogi. -deleteoznacza go usunąć.

UWAGA: to polecenie usunie wszystkie pliki (oprócz plików pdf, ale także pliki ukryte) w bieżącym katalogu, a także we wszystkich podkatalogach. !musi przyjść wcześniej -name. po prostu -namebędzie zawierać tylko .pdf, podczas gdy -inamebędzie zawierać zarówno .pdfi.PDF

Aby usunąć tylko w bieżącym katalogu, a nie w podkatalogach, dodaj -maxdepth 1:

find . -maxdepth 1 -type f ! -iname "*.pdf" -delete
Edward Torvalds
źródło
Dziękuję za odpowiedź. Czy możesz mi pomóc trochę zrozumieć składnię? .oznacza „i”? !oznacza „oprócz” -nameoznacza, że ​​chcesz wykluczyć za pomocą parametru nazwy, a następnie -deleteczy należy podjąć działanie po znalezieniu? Więc szuka wszystkiego oprócz „* .pdf” i usuwa je? A może źle zrozumiałem?
jessenorton
.oznacza bieżący katalog. !oznacza, że ​​weźmiesz wszystkie pliki oprócz tego .pdfna końcu. -deleteoznacza go usunąć. czy jestem teraz czysty?
Edward Torvalds,
@terdon Starkers powiedział, że nie ma podkatalogów. poczekaj źle zredaguj moją odpowiedź, aby była szersza
Edward Torvalds
+1 Powinieneś dołączyć -maxdepth 1parametr na początek. Następnie zasugeruj usunięcie parametru, jeśli chcesz usunąć rekurencyjnie.
Tulains Córdova,
3
ten zwrócił moją uwagę, że należy używać -inamezamiast -name, albo z plików .PDFjako rozszerzenie będzie przemknąć.
muru
43

Dzięki bashrozszerzeniu globowania powłoki możesz usunąć wszystkie pliki z rozszerzeniami innymi niż .pdfza pomocą

rm -- *.!(pdf)

Jak zauważono w @pts, --znaki wskazują koniec dowolnych opcji poleceń, dzięki czemu polecenie jest bezpieczne w rzadkich przypadkach plików, których nazwy zaczynają się od -znaku.

Jeśli chcesz usunąć pliki bez żadnego rozszerzenia, a także pliki z rozszerzeniami innymi niż .pdf, to jak wskazał @DennisWilliamson, możesz użyć

rm -- !(*.pdf)

Rozszerzone globowanie powinno być domyślnie włączone, ale jeśli nie, możesz to zrobić za pomocą

shopt -s extglob

Zwłaszcza, jeśli zamierzasz użyć tego w skrypcie, ważne jest, aby pamiętać, że jeśli wyrażenie nie pasuje do niczego (tj. Jeśli w katalogu nie ma plików innych niż pdf), wówczas domyślnie glob zostanie przekazany do pliku rmpolecenie, co powoduje błąd podobny do

rm: cannot remove `*.!(pdf)': No such file or directory

Możesz zmodyfikować to domyślne zachowanie za pomocą nullglobopcji powłoki, jednak ma to swój problem. Aby uzyskać dokładniejszą dyskusję, zobacz NullGlob - Wiki Grega

steeldriver
źródło
Lepsze podejście IMO.
Takkat
Co z plikami bez rozszerzenia? FWIW, w zsh torm *~*.pdf
Emil Jeřábek
1
Umieściłbym kropkę w nawiasach.
Dennis Williamson,
4
Ach, gwiazdka powinna także wejść do środka: !(*.py). Prawdopodobnie, jeśli OP chce pozostać tylko plikami „.pdf”, pliki bez rozszerzeń również powinny zostać usunięte i nie powinny być ignorowane.
Dennis Williamson,
1
To podejście jest prostsze i staranniejsze niż przyjęta odpowiedź.
Peter
18

Usuń do kosza :

$ cd <the directory you want>
$ gvfs-trash !(*.pdf)

Lub za pomocą mvpolecenia (ale w ten sposób nie można przywrócić go z Kosza, ponieważ nie rejestruje on informacji .trashinfo, więc oznacza to, że przeniesiono pliki do miejsca docelowego, w którym znajduje się poniżej).

mv !(*.pdf) ~/.local/share/Trash/files
αғsнιη
źródło
6
Takie podejście jest znacznie bezpieczniejsze niż bezpośrednie użycie rm.
Seth
14

Najłatwiejsze podejście: Utwórz gdzieś inny katalog (jeśli usuwasz tylko w jednym katalogu, a nie rekurencyjnie, może to być nawet podkatalog); przenieś tam wszystkie pliki .pdf; usuń wszystko inne; cofnij plik pdf do tyłu; usuń katalog pośredni.

Szybko, łatwo, możesz dokładnie zobaczyć, co robisz. Upewnij się tylko, że katalog pośredni znajduje się na tym samym urządzeniu co katalog, który czyścisz, aby ruchy były nazwami, a nie kopiami!

Nocnik
źródło
4
+1 Znowu za komentarz, który ma sens dla początkującego użytkownika, który prawie na pewno nie spowoduje przypadkowego usunięcia plików.
trognanders,
4

Użyj GLOBIGNORE bash:

GLOBIGNORE=x.pdf:a.pdf
rm *
unset GLOBIGNORE

Ze strony podręcznika użytkownika bash:

GLOBIGNORE:

            Rozdzielona dwukropkami lista wzorców definiujących zestaw
            nazw plików, które mają zostać zignorowane przez rozwinięcie nazwy ścieżki.

Szybki test:

mkdir /tmp/foooooo
cd /tmp/foooooo
touch x.pdf y.zip z.mp3 a.pdf
GLOBIGNORE=x.pdf:a.pdf
ls -1 *

Wynik:

y.zip
z.mp3
Cyrus
źródło
3

Bądź ostrożny i skomponuj: użyj xargs

Oto podejście mi się podoba, bo to pozwala mi być bardzo ostrożny: komponować sposób, aby pokazać tylko te pliki, które chcę usunąć, a następnie wysłać je do rmkorzystania xargs. Na przykład:

  • ls pokazuje mi wszystko
  • ls | grep pdfpokazuje mi pliki, które chcę zachować. Hmm
  • ls | grep -v pdfpokazuje coś przeciwnego: wszystko oprócz tego, co chcę zachować. Innymi słowy, pokazuje listę rzeczy, które chcę usunąć. Mogę to potwierdzić, zanim zrobię coś niebezpiecznego.
  • ls | grep -v pdf | xargs rmwysyła dokładnie tę listę do rmusunięcia

Jak powiedziałem, podoba mi się to przede wszystkim ze względu na bezpieczeństwo: nie przypadek rm *dla mnie. Dwie inne zalety:

  • Składa się; możesz użyć lslub, findaby uzyskać początkową listę, jak wolisz. Możesz zawęzić wszystko, co chcesz, w trakcie zawężania tej listy - inną grep, inną awklub cokolwiek innego. Jeśli chcesz usunąć tylko pliki, których nazwy zawierają kolor, możesz zbudować go w ten sam sposób.
  • Możesz użyć każdego narzędzia do jego głównego celu. Wolę używać finddo wyszukiwania i rmusuwania, zamiast pamiętać, że findakceptuje -deleteflagę. A jeśli to zrobisz, ponownie możesz skomponować alternatywne rozwiązania; może zamiast tego rmmożesz utworzyć trashpolecenie, które przenosi plik do kosza (umożliwiając „usunięcie”) i potokuje do niego zamiast rm. Nie potrzebujesz findobsługi tej opcji, po prostu do niej potokujesz.

Aktualizacja

Zobacz komentarze @pabouk, aby dowiedzieć się, jak to zmienić, aby obsługiwać niektóre przypadki krawędzi, takie jak podziały linii w nazwach plików, nazwy plików my_pdfs.zipitp.

Nathan Long
źródło
4
Zauważyłem tutaj trzy problemy: a) Wyklucza każdy plik zawierający pdfdowolne miejsce w jego nazwie. --- b) Usunie pliki PDF, jeśli dowolna litera w sufiksie jest wielka. --- c) Nie jest dobrym pomysłem używanie wyjścia ls. Nie będzie działać z nazwami plików zawierającymi znaki nowej linii. Niektóre implementacje lszamiany znaków specjalnych, np. Tab by ?. --- Lepiej jest użyć: find -maxdepth 1 -print0. (nie tak krótki jak ls:) ----- Aby rozwiązać a) ib) użyj grep -vi '\.pdf$'--- kompletne (ale tylko GNU) rozwiązanie:find -maxdepth 1 -print0 | grep -viz '\.pdf$' | xargs -0 rm
pabouk
1
Rozumiem, że miałeś na myśli rozwiązanie jako „interaktywny” proces z wieloma ręcznymi iteracjami, ale kontrole nie będą przydatne w przypadku długich list plików, a wyżej wymienione problemy mogą łatwo przeoczyć błędy.
pabouk
1
@pabouk dobre punkty; prawdziwy świat zawsze komplikuje rzeczy, a twoje poprawki są pomocne. :) Ale nadal uważam, że to ogólne podejście jest najlepsze. Jeśli jest zbyt wiele plików, aby wizualnie potwierdzić wszystko, możesz | head -20przynajmniej sprawdzić, czy wygląda mniej więcej poprawnie, a jeśli tak rm my_pattern, nie masz szansy na zauważenie dużego błędu.
Nathan Long,
1
Możesz pokazać, że pokażą ci pliki, zanim je również usuniesz, pomiń -delete i po prostu użyj find . -type f ! -name "*.pdf"do drukowania na konsoli, lub potokuj do mniejszej lub pliku. [a następnie potokuj do xargs do rm, jeśli chcesz, tak jak komentarze pabouk (z -print0 | ... -0 dla dziwnych nazw plików)]
Xen2050
3

Zwykle rozwiązuję takie problemy z interaktywnego interpretera Pythona:

mic@mic ~ $ python
>>> import os
>>> for f in os.listdir('.'):
...   if not f.endswith('.pdf'):
...     os.remove(f)

Może być dłuższy niż jednowarstwowy z findlub xargs, ale jest bardzo odporny i wiem dokładnie, co robi, bez konieczności wcześniejszego badania.

mic_e
źródło
Dla tych, którzy coraz bardziej denerwują się z każdą dodatkową linią, moglibyśmy stworzyć jedną:for item in [f for f in os.listdir('.') if not f.endswith('.pdf')]: os.remove(item)
Jacob Vlijm
python -c "import os; for f in os.listdir('.'): if not f.endswith('.pdf'): os.remove(f)"
mic_e
[os.remove(f) for f in os.listdir('.') if not f.endswith('.pdf')]
mic_e
miły! drugi daje mi błąd składniowy, nie rozumiem dlaczego.
Jacob Vlijm,
dziwne; działa zarówno z Pythonem 3.4, jak i Pythonem 2.7 w moim systemie.
mic_e
2

lepszą odpowiedzią (w porównaniu do mojej poprzedniej odpowiedzi) na to pytanie będzie użycie potężnego filepolecenia.

$ file -i abc.pdf
abc: application/pdf; charset=binary

teraz twój problem:

cd <the directory you want to search in>
for var in ./*
do
if file -i "$var" | grep -q 'application/pdf\;'
then
echo "$var"
fi
done

zadaniem forpolecenia jest podanie plików w bieżącym katalogu w postaci zmiennej $var. if-thenpolecenie wyświetla nazwy plików pdf, biorąc status wyjścia 0z file -i "$var" | grep -q 'application/pdf\;'polecenia, daje status wyjścia 0tylko wtedy, gdy znajdzie pliki pdf.

Edward Torvalds
źródło
1
rm $(ls -lo|grep -v [Pp][Dd][Ff]$|awk '{print $7}')

Ostrzeżenie! Lepiej najpierw spróbuj

ls -l $(ls -lo|grep -v [Pp][Dd][Ff]$|awk '{print $7}')
Martín-Blas Pérez Pinilla
źródło
2
Ugh, jest to błędne na wiele sposobów: smallo.ruhr.de/award.html#ls , smallo.ruhr.de/award.html#grep i całkowicie ignoruje nazwy plików z białymi spacjami lub znakami specjalnymi.
David Foerster,
1
Naprawdę powinieneś używać -iz grepdo dopasowywania bez rozróżniania wielkości liter.
muru
1
rm -i -- !(*@(a|x).pdf)

Czytaj jako, usuń wszystkie pliki, które nie są a.pdflub x.pdf.

Działa poprzez wykorzystanie 2 wydłużonych globs zewnętrzna !()negacji zawartego glob, który z kolei wymaga, aby do wzorca, musi być zgodny z jednym lub więcej alub xstanie przed .pdfkońcówką. Zobacz glob # extglob .

$ ls -a
.dotfile1 .dotfile2 a.pdf x.pdf y.zip z.mp3

$ echo -- !(a.pdf)
-- x.pdf y.zip z.mp3

$ echo -- !(x.pdf)
-- a.pdf y.zip z.mp3

$ echo -- !(a.pdf|x.pdf)
-- y.zip z.mp3

$ echo -- !(@(a|x).pdf)   # NOTE.that this matches the .dotfiles* as well
-- . .. .dotfile1 .dotfile2 y.zip z.mp3

$ echo -- !(*@(a|x).pdf)  # but this doesn't
-- y.zip z.mp3

$ echo rm -i -- !(*@(a|x).pdf)
rm -i -- y.zip z.mp3
shalomb
źródło
1

przenośny sposób powłoki

$ ksh -c 'for i in ./*; do case $i in *.pdf)continue;; *)rm "$i";; esac;done'

Dość dużo POSIX i kompatybilny z dowolnym powłoce Bourne'a Style ( ksh, bash, dash). Dobrze nadaje się do przenośnych skryptów i gdy nie możesz użyć bashrozszerzonego globowania powłoki.

perl:

$ perl -le 'opendir(my $d,"."); foreach my $f (grep(-f && !/.pdf/ , readdir($d))){unlink $f};closedir $d'                                                             

Lub nieco czystszy:

$ perl -le 'opendir(my $d,"."); map{ unlink $_ } grep(-f "./$_" && !/.pdf/ , readdir($d));closedir $d'

alternatywny python

python -c 'import os;map(lambda x: os.remove(x), filter(lambda x: not x.endswith(".pdf"),os.listdir(".")))'
Sergiy Kolodyazhnyy
źródło
0

Uważaj na to, co usuwasz!

Bezpiecznym sposobem przetestowania go przed próbą usunięcia jest przetestowanie go najpierw ls, ponieważ niektóre nieprzechwycone zachowania mogą usuwać niechciane pliki. Możesz to zrobić bezpośrednio poza katalogiem. lsjest podobny do rm, więc:

ls sub/path/to/files/!(*.pdf)

Spowoduje to wyświetlenie listy

y.zip
z.mp3

Teraz możesz zobaczyć, co usuwasz, i możesz je bezpiecznie usunąć:

rm sub/path/to/files/!(*.pdf)

I to wszystko. Możesz używać symboli wieloznacznych, *aby być bardziej selektywnym, tak jak przechowywanie tylko dokumentów kursu programowania:

rm sub/path/to/files/!(*programming*)
KeitelDOG
źródło