Мне нужно удалить все файлы с расширением .gif, кроме одного файла с именем «filename.gif». Каков оптимальный способ сделать это в терминале?
Команда rm *.gif
удаляет все файлы gif, включая файл filename.gif
.
Вот простое решение, которое необходимо, вероятно, использовать:
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 . -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
и файлы в глубоком sub-sub-sub... каталоги .
) был бы удален!
Для получения дополнительной информации см. страницы руководства для bash
и sh
и поскольку команды использовали в этих примерах: mv
, rm
, [
, и (особенно) find
.
Если вы используете bash
(оболочка по умолчанию), опция оболочки extglob
позволяет вам использовать расширенный синтаксис сопоставления с образцом. Чтобы включить его, используйте встроенную команду shopt
:
shopt -s extglob
(я включаю эту строку в мой файл .bashrc
.)
Помимо прочего, он предоставляет доступ к [ 116] оператор, который соответствует любому шаблону, не находящемуся внутри скобок. Для вашей цели:
rm !(filename).gif
Дополнительную информацию можно найти в man bash
в разделе «Сопоставление с образцом».
Откройте Terminal with Ctrl + Высокий звук + T и тип:
find . -type f -name "*.gif" -and -not -name "filename.gif" -exec rm -vf {} \;
Разница между -delete
и -exec rm -vf {} \;
это со второй опцией, Вы сможете видеть, какие файлы были удалены, из-за -v
флаг. Это - что-то, что не может быть, покончили -delete
опция. (-name -delete
распечатает имена файлов, но rm
с -v
показывает, был ли каждый файл успешно удален.)
Это переменная 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
, но выиграл не совпадают с .
или ..
, поэтому вы не затронете родительский каталог таким образом.