Удаление файлов с определенным расширением, кроме одного файла из терминала

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? Цикл for *хорошо обрабатывает файлы , потому что вы использовали двойные кавычки.
Flimm
28

Если вы используете bash (оболочка по умолчанию), extglobопция оболочки позволяет вам использовать расширенный синтаксис сопоставления с образцом. Чтобы включить его, используйте shoptвстроенную команду:

shopt -s extglob

(Я включил эту строку в мой .bashrcфайл.)

Помимо прочего, он предоставляет доступ к !() оператору, который соответствует любому шаблону, не входящему в состав скобок. Для вашей цели:

rm !(filename).gif

Более подробная информация доступна в man bashразделе «Сопоставление с образцом».

eswald
источник
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показом, если каждый файл был успешно удален.)

efthialex
источник
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, но он не будет соответствовать .или .., так что вы не будет влиять на родительский каталог , что путь.

Мур
источник