Usuwanie plików z określonym rozszerzeniem oprócz jednego pliku z terminala

36

Muszę usunąć wszystkie pliki z rozszerzeniem .gif, z wyjątkiem jednego pliku o nazwie powiedzieć „nazwa_pliku.gif”. Jaki jest optymalny sposób na zrobienie tego w terminalu?

Polecenie rm *.gifusuwa wszystkie pliki gif, w tym plik filename.gif.

Seth
źródło

Odpowiedzi:

66

Oto proste rozwiązanie, którego prawdopodobnie powinieneś użyć:

mv filename.gif filename.gif.keep
rm *.gif
mv filename.gif.keep filename.gif

W tym .keeprozszerzeniu nie ma nic specjalnego , dzięki czemu nazwa pliku tymczasowo się nie kończy .gif.

Jeżeli nie musi zmienić nazwę pliku (i istnieje skryptów sytuacje, w których jest to ważne):

for X in *.gif; do
    if [ "$X" != "filename.gif" ]; then
        rm "$X"
    fi
done

Możesz też napisać to krócej:

for X in *.gif; do [ "$X" != "filename.gif" ] && rm "$X"; done

Zamiast tego możesz użyć find; to bardzo silny, to może rozważyć to bardziej czytelne i lepiej uchwyty dziwne nazwy plików z postaciami takimi jak *na nich .

find . -maxdepth 1 -not -name 'filename.gif' -name '*.gif' -delete

Użyłem -notoperatora dla czytelności, ale jeśli zgodność z POSIX jest ważna - jeśli nie używasz GNU find, lub jeśli jest to skrypt, który zamierzasz rozpowszechniać wśród innych lub uruchamiać na różnych systemach - powinieneś !zamiast tego użyj operatora:

find . -maxdepth 1 ! -name 'filename.gif' -name '*.gif' -delete

Jedną przydatną rzeczą findjest to, że możesz łatwo zmodyfikować polecenie, aby nie rozróżniała wielkości liter, tak aby znajdowało i usuwało pliki z rozszerzeniami takimi jak .GIF:

find . -maxdepth 1 -not -name 'filename.gif' -iname '*.gif' -delete

Pamiętaj, że użyłem -inamezamiast -namewzorca wyszukiwania, *.gifale go nie użyłem filename.gif. Prawdopodobnie wiesz dokładnie, jak nazywa się Twój plik, i -inamebędzie pasował do naprzemiennej wielkości liter nie tylko w rozszerzeniu , ale w dowolnym miejscu w nazwie pliku.

Wszystkie te rozwiązania usuwają tylko pliki znajdujące się natychmiast w bieżącym katalogu . Nie usuwają plików nie zawartych w bieżącym katalogu i nie usuwają plików znajdujących się w podkatalogach bieżącego katalogu.

Jeśli chcesz usunąć pliki zawarte w bieżącym katalogu (tzn. W podkatalogach i podkatalogach tych podkatalogów itd. - pliki zawarte w bieżącym katalogu lub dowolnym z jego potomków), używaj find bez maxdepth -1 :

find . -not -name 'filename.gif' -name '*.gif' -delete

Uważaj na to!

Możesz także ustawić inne wartości za pomocą -maxdepth. Na przykład, aby usunąć pliki z bieżącego katalogu oraz jego dzieci i wnuków, ale nie głębiej:

find . -maxdepth 3 -not -name 'filename.gif' -name '*.gif' -delete

Tylko upewnij się, że nigdy nie stawiasz na -deletepierwszym miejscu, przed innymi wyrażeniami! Zobaczysz, że zawsze umieszczam -deletena końcu. Jeśli umieścisz go na początku, zostanie najpierw oceniony, a wszystkie pliki poniżej .(w tym pliki, które się nie kończą .gifi pliki w głębokich podkatalogach podrzędnych ... katalogów .) zostaną usunięte!

Aby uzyskać więcej informacji , zobacz strony podręcznika dla bashi shoraz do komend użytych w tych przykładach: mv, rm, [, i (przede wszystkim) find.

Eliah Kagan
źródło
1
Dzięki Eliaszu. Zrobiłem to podobnie do pierwszej metody, którą zasugerowałeś, tj. Przekonwertować plik w innym formacie i przekonwertować go z powrotem. Ale to wydawało się nieco nieoptymalne i nieczyste. Podoba mi się drugie podejście, które zasugerowaliście Ty i @efthialex, i używam go teraz. Dzięki!
1
@Marvis Cieszę się, że to spełniło twoje potrzeby. Nawiasem mówiąc, rozszerzyłem tę odpowiedź o informacje o innej metodzie (lub klasie metod) find, która jest bardzo wszechstronna.
Eliah Kagan
3
Dzięki za szczegółową odpowiedź findto naprawdę dobre znalezisko.
1
+1 za odpowiedź „zdrowo” (przenieś-usuń-przenieś).
Tu-Reinstate Monica-dor duh
Nit-pick: jak radzi findsobie z dziwnymi nazwami plików *w nich lepiej niż w pętli for? Pętla for dobrze radzi sobie z plikami *, ponieważ użyłeś podwójnych cudzysłowów.
Flimm
28

Jeśli używasz bash(domyślna powłoka), extglobopcja powłoki umożliwia użycie rozszerzonej składni dopasowania wzorca. Aby go włączyć, użyj shoptwbudowanego polecenia:

shopt -s extglob

(Uwzględniam ten wiersz w moim .bashrcpliku).

Zapewnia między innymi dostęp do !()operatora, który pasuje do dowolnego wzoru spoza parens. Do twojego celu:

rm !(filename).gif

Więcej informacji jest dostępnych w man bash„Dopasowywanie wzorców”.

Eswald
źródło
1
Doskonała odpowiedź! Jest specyficzny dla bash, więc może nie być odpowiedni do przenośnych skryptów, ale interaktywnych zadań, a nawet niektórych skryptów, jest to najlepsza droga do zrobienia. Niesie dodatkową złożoność, którą extglobnależy włączyć, ale nic złego się nie dzieje, jeśli jest wyłączone (w takim przypadku to po prostu nie działa). I wydaje się, że jest domyślnie włączony, nawet jeśli nie mam shopt -s extglobżadnego z moich (lub globalnych) plików konfiguracyjnych. Jeśli istnieje proste wyjaśnienie, dlaczego to działa dla mnie po wyjęciu z pudełka, możesz dodać to do tej odpowiedzi; lub mogę opublikować nowe pytanie.
Eliah Kagan
13

Otwórz terminal za pomocą Ctrl+ Alt+ Ti wpisz:

find . -type f -name "*.gif" -and -not -name "filename.gif" -exec rm -vf {} \;

Różnica między -deletei -exec rm -vf {} \;polega na tym, że dzięki drugiej opcji będziesz mógł zobaczyć, które pliki zostały usunięte z powodu -vflagi. Tego nie da się zrobić z tą -deleteopcją. ( -name -deleteWypisze nazwy plików, ale rmz -vobjawia jeśli każdy plik został pomyślnie usunięty.)

efthialex
źródło
1

Jest GLOBIGNOREzmienna. Od man bash:

GLOBIGNORE
      A colon-separated list of patterns defining the set of filenames
      to be ignored by pathname expansion.  If a filename matched by a
      pathname expansion pattern also matches one of the  patterns  in
      GLOBIGNORE, it is removed from the list of matches.

Prostym sposobem jest po prostu ustawienie GLOBGINOREnazwy pliku, o której mowa:

$ touch {a,b,c,d}.png
$ echo *.png
a.png b.png c.png d.png
$ GLOBIGNORE=c.png
$ echo *.png
a.png b.png d.png

W twoim przypadku:

GLOBIGNORE=filename.gif; rm *.gif

Oczywiście, ponieważ GLOBIGNOREzawiera wzory, możesz go użyć, gdy chcesz wykluczyć wzór:

$ GLOBIGNORE='[a-c].png'; echo *.png
d.png
$ touch bd.png; GLOBIGNORE='?.png'; echo *.png
bd.png

Zaletą (?!) GLOBIGNOREJest to, że ustawienie to automatycznie wyklucza .i nie.. jest dopasowywane oraz umożliwia dotglob:

The  GLOBIGNORE shell variable may be used to restrict the set of file‐
names matching a pattern.  If GLOBIGNORE is set, each matching filename
that also matches one of the patterns in GLOBIGNORE is removed from the
list of matches.  The filenames ``.''  and ``..''  are  always  ignored
when  GLOBIGNORE is set and not null.  However, setting GLOBIGNORE to a
non-null value has the effect of enabling the dotglob shell option,  so
all other filenames beginning with a ``.''  will match.  To get the old
behavior of ignoring filenames beginning with a ``.'', make ``.*''  one
of  the  patterns  in  GLOBIGNORE.  The dotglob option is disabled when
GLOBIGNORE is unset.

Tak więc prosty sposób usuwania wszystkich plików i folderów w katalogu:

GLOBIGNORE=.; rm -r *

*Dopasuje nazwy plików rozpoczynające się ., gdyż GLOBIGNOREwłączone dotglob, ale nie będzie pasować .albo .., więc nie wpłynie na katalog nadrzędny w ten sposób.

muru
źródło