Видалення файлів з певним розширенням, крім одного файлу з терміналу


36

Мені потрібно видалити всі файли з розширенням .gif, за винятком одного файлу з ім'ям "filename.gif". Який оптимальний спосіб зробити це в терміналі?

Команда rm *.gifвидаляє всі gif-файли, включаючи файл filename.gif.

Відповіді:


66

Ось просте рішення, яке ви, ймовірно, повинні використовувати:

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

У .keepрозширенні нічого особливого , це просто робить так, щоб ім’я файлу тимчасово не закінчувалося .gif.

Якщо ви не повинні перейменовувати файл (і є сценарії, коли це важливо):

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

Або ви можете написати це коротше так:

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

Ви можете скористатися findзамість цього; це дуже потужний, ви могли б розглянути його більш зручним для читання, і це краще ручок дивних імен файлів з символами , як *в них .

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

Я використовував -notоператора для читабельності, але якщо важлива відповідність POSIX - якщо ви не використовуєте GNU find, або якщо це сценарій, який ви маєте намір перерозподілити іншим або запустити в різних системах - вам слід використовувати !замість оператора:

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

Зручна річ у findтому, що ви можете легко змінити команду на нечутливість регістру, щоб вона знаходила і видаляла файли з розширеннями як .GIF:

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

Зверніть увагу, що я використовував -inameзамість -nameсхеми пошуку, *.gifале я не використовував її filename.gif. Імовірно, ви точно знаєте, як називається ваш файл, і -inameбуде відповідати альтернативній великій літери не тільки в розширенні , але і в будь-якому місці імені файлу.

Усі ці рішення видаляють лише файли, що знаходяться негайно в поточному каталозі . Вони не видаляють файли, що не містяться в поточному каталозі, і не видаляють файли, що знаходяться в підкаталогах поточного каталогу.

Якщо ви хочете видалити файли скрізь, що містяться в поточному каталозі (тобто в тому числі в підкаталогах і в підкаталогах цих підкаталогів тощо) - файли, що містяться в поточному каталозі або будь-якому з його нащадків), використовуйте find без maxdepth -1 :

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

Будьте обережні з цим!

Ви також можете встановити інші значення за допомогою -maxdepth. Наприклад, для видалення файлів у поточному каталозі та його дітей та онуків, але не будь-які глибші:

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

Просто переконайтеся, що ви ніколи не ставите -deleteперше, перед іншими виразами! Ви побачите, що я завжди ставив -deleteу кінці. Якщо ви поставите його на початку, воно буде оцінено спочатку, і всі файли в розділі .(включаючи файли, що не закінчуються, .gifта файли в глибоких підкаталогах підподібних підрозділів .) будуть видалені!

Для отримання додаткової інформації зверніться до сторінок довідника bashі shі для команд , які використовуються в цих прикладах: mv, rm, [, і (особливо) find.


1
Дякую, Елія. Я зробив це аналогічно першому запропонованому вами методу, тобто конвертувати файл в інший формат і перетворити його назад. Але це здавалося трохи неоптимальним і нечистим. Мені подобається другий підхід, який ви запропонували @efthialex, і я зараз його використовую. Спасибі!

1
@Marvis Я радий, що це відповіло вашим потребам. До речі, я розширив цю відповідь інформацією про інший метод (або клас методів) використання find, який є дуже універсальним.
Елія Каган

3
Дякую за детальну відповідь find- це справді хороша знахідка.

1
+1 за відповідь здорового глузду (переміщення-видалення-переміщення).
tu-Reinstate Monica-dor duh

Nit-pick: як краще findобробляти дивні назви файлів *у них, ніж for-loop? Цикл for обробляє файли *штрафом, оскільки ви використовували подвійні лапки.
Flimm

28

Якщо ви використовуєте bash(оболонка за замовчуванням), extglobопція оболонки дозволяє використовувати розширений синтаксис відповідності шаблону. Щоб увімкнути це, використовуйте shoptвбудовану команду:

shopt -s extglob

(Я включаю цей рядок у свій .bashrcфайл.)

Крім усього іншого, він надає доступ !()оператору, який відповідає будь-якому шаблону, що не знаходиться в межах паролів. Для вашої мети:

rm !(filename).gif

Більш детальна інформація доступна в man bashрозділі "Узгодження шаблонів".


1
Відмінна відповідь! Це специфічно для bash, тому може не підходити для переносних сценаріїв, але інтерактивні завдання та навіть деякі сценарії - це найкращий спосіб зробити рукою вниз. Він несе в собі додаткову складність, яку extglobпотрібно включити, але нічого поганого не відбувається, якщо його відключити (у такому випадку це просто не працює). І це, здається, увімкнено за замовчуванням, навіть якщо я не маю shopt -s extglobжодного з моїх (або глобальних) файлів конфігурації. Якщо є просте пояснення, чому це працює для мене поза коробкою, ви можете додати його до цієї відповіді; або я можу поставити нове запитання.
Елія Каган

13

Відкрийте термінал з Ctrl+ Alt+ Tі введіть:

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

Різниця між -deleteі -exec rm -vf {} \;полягає в тому, що з другого варіанту ви зможете побачити, які файли було видалено через -vпрапор. Це те, що неможливо зробити з -deleteопцією. ( -name -deleteбуде надруковано імена файлів, але rmз -vвиявленням, чи вдало видалено кожен файл .)


1

Там є GLOBIGNOREзмінна. Від 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.

Таким чином, простий спосіб - просто встановити GLOBGINOREвказане ім'я файлу:

$ 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

Отже, у вашому випадку:

GLOBIGNORE=filename.gif; rm *.gif

Звичайно, оскільки GLOBIGNOREмістить шаблони, ви можете користуватися нею, коли потрібно виключити шаблон:

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

Перевагою (?!) GLOBIGNOREЄ те, що це... налаштування автоматично виключає та не відповідає, і це дозволяє 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.

Отже, простий спосіб видалення всіх файлів і папок у каталозі:

GLOBIGNORE=.; rm -r *

Ім'я *буде відповідати назви файлів, починаючи з ., оскільки воно GLOBIGNOREввімкнено dotglob, але воно не збігається .або .., таким чином, ви не вплинете на батьківський каталог таким чином.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.