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

У меня есть каталог, который содержит следующее:

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

Я хочу удалить все файлы, кроме x.pdf и a.pdf. Как это сделать с терминала? Нет никаких подкаталогов, поэтому нет необходимости в какой-либо рекурсии.

1
задан 18 May 2015 в 12:36

10 ответов

С расширенным чередованием оболочки bash вы можете удалить любые файлы с расширениями, отличными от .pdf, используя

rm -- *.!(pdf)

Как отмечено @pts, символы -- указывают на конец любых параметров команды, сделайте команду безопасной в редких случаях с файлами, имена которых начинаются с символа -.

Если вы хотите удалять файлы без каких-либо расширений, а также с расширениями, отличными от [ f9], то, как указал @DennisWilliamson, вы можете использовать

rm -- !(*.pdf)

. Расширенное глобальное включение должно быть включено по умолчанию, но если вы не можете сделать это, используя

shopt -s extglob
[ ! d4]

Особенно, если вы намерены использовать это внутри скрипта, важно отметить, что если выражение не соответствует чему-либо (т. е. если в каталоге нет файлов без PDF-файлов), то по умолчанию glob будет передан без изменений в команду rm, что приведет к ошибке, например

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

. Вы можете изменить это поведение по умолчанию, используя опцию оболочки nullglob, однако это имеет свою проблему. Для более подробного обсуждения см. NullGlob - Wiki

Грега]
41
ответ дан 24 May 2018 в 01:24
  • 1
    Лучший подход ИМО. – Takkat 1 December 2014 в 16:21
  • 2
    Как насчет файлов без расширения? FWIW, в zsh это rm *~*.pdf – Emil Jeřábek 1 December 2014 в 19:18
  • 3
    Я бы поставил точку в круглые скобки. – Dennis Williamson 2 December 2014 в 00:39
  • 4
    Ах, звездочка должна также войти внутрь: !(*.py). Кроме того, предположительно, если OP хочет только «.pdf», файлы остаются, а затем файлы без расширений также должны быть удалены и не игнорироваться. – Dennis Williamson 2 December 2014 в 00:50
  • 5
    Этот подход более простой и опрятный, чем принятый ответ. – Peter 3 December 2014 в 14:31

Удалить в корзину:

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

Или с помощью команды mv (но таким образом вы не можете восстановить ее из корзины, поскольку она не записывает информацию .trashinfo, поэтому это означает, что вы переместили свой файлы в пункт назначения, где он следующий).

mv !(*.pdf) ~/.local/share/Trash/files
17
ответ дан 24 May 2018 в 01:24
  • 1
    Этот подход гораздо безопаснее, чем прямое использование rm. – Seth♦ 3 December 2014 в 00:13

Самый простой подход: создайте другой каталог где-нибудь (если вы удаляете только один каталог, а не рекурсивно, он может даже быть подкаталогом); переместите все .pdf; удалять все остальное; переместите обратно pdf; удалите промежуточный каталог.

Быстро, легко, вы можете точно видеть, что вы делаете. Просто убедитесь, что промежуточный каталог находится на том же устройстве, что и очищаемый каталог, так что хосты переименовываются, а не копии!

14
ответ дан 24 May 2018 в 01:24
  • 1
    +1 Снова для комментария, который имеет смысл для начинающего пользователя, что почти наверняка не приведет к непреднамеренному удалению файлов. – Bailey S 2 December 2014 в 22:59

Используйте GLOBIGNORE из bash:

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

Из справочной страницы bash:

GLOBIGNORE: A colon-separated list of patterns defining the set of filenames to be ignored by pathname expansion.

Быстрая проверка:

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

Выход:

y.zip z.mp3
4
ответ дан 24 May 2018 в 01:24

Я обычно решаю такие проблемы из интерактивного интерпретатора Python:

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

Он может быть длиннее однострочного с find или xargs, но он чрезвычайно устойчив, и я знаю точно, что он делает, не имея необходимости сначала исследовать его.

3
ответ дан 24 May 2018 в 01:24
  • 1
    Для тех, кто становится все более нервным с каждой дополнительной линией, мы могли бы сделать это одним: for item in [f for f in os.listdir('.') if not f.endswith('.pdf')]: os.remove(item) – Jacob Vlijm 4 December 2014 в 20:59
  • 2
    [F1] – mic_e 4 December 2014 в 21:36
  • 3
    [F1] – mic_e 4 December 2014 в 21:37
  • 4
    хороший! второй дает мне синтаксическую ошибку, не понимаю почему. – Jacob Vlijm 4 December 2014 в 21:48
  • 5
    странный; он работает как с python 3.4, так и с python 2.7 в моей системе. – mic_e 4 December 2014 в 21:56

Будьте осторожны и соберите: используйте xargs

Вот такой подход, который мне нравится, потому что он позволяет мне быть очень осторожным: составить способ показать только файлы, которые я хочу удалить, а затем отправить их в rm используя xargs. Например:

ls показывает мне все ls | grep pdf показывает мне файлы, которые я хочу сохранить. Хм. ls | grep -v pdf показывает противоположное: все, кроме того, что я хочу сохранить. Другими словами, он показывает список вещей, которые я хочу удалить. Я могу подтвердить это, прежде чем делать что-нибудь опасное. ls | grep -v pdf | xargs rm отправляет именно этот список в rm для удаления

Как я уже сказал, мне в основном нравится это для обеспечения безопасности: для меня нет случайных rm *. Два других преимущества:

ls показывает мне все Вы можете использовать каждый инструмент для своей основной цели. Я предпочитаю использовать find для поиска и rm для удаления, в отличие от необходимости помнить, что find принимает флаг -delete. И если вы это сделаете, опять же, вы можете составить альтернативные решения; возможно, вместо rm вы могли бы создать команду trash, которая перемещает файл в корзину (разрешая «undeletion») и pipe на это вместо rm. Вам не нужно иметь find поддержку этого параметра, вы просто подключаетесь к нему.

Обновить

См. комментарии от @pabouk о том, как изменить это, чтобы обрабатывать некоторые случаи краев, такие как разрывы строк в именах файлов, имена файлов, такие как my_pdfs.zip и т. д.

2
ответ дан 24 May 2018 в 01:24
  • 1
    Здесь я заметил три проблемы: a) Он исключает любой файл, содержащий pdf в любом месте его имени. --- b) Он удалит файлы PDF, если любая из букв в суффиксе является верхним регистром. --- c) Не рекомендуется использовать вывод ls. Он не будет работать с именами файлов, содержащих новые строки. Некоторые реализации ls заменяют специальные символы, например. вкладку ?. --- Лучше использовать: find -maxdepth 1 -print0. (не так коротко, как ls :) ----- Для решения а) и б) используйте grep -vi '\.pdf$' --- полное (но только для GNU) решение: find -maxdepth 1 -print0 | grep -viz '\.pdf$' | xargs -0 rm – pabouk 2 December 2014 в 02:12
  • 2
    Я понимаю, что вы имели в виду решение как "интерактивное" процесс с несколькими ручными итерациями, но проверки вряд ли пригодятся для длинных списков файлов, и упомянутые выше проблемы могут легко устранить ошибки. – pabouk 2 December 2014 в 02:22
  • 3
    @pabouk хорошие моменты; реальный мир всегда усложняет ситуацию, и ваши исправления полезны. :) Но я до сих пор считаю, что этот общий подход лучше всего. Если слишком много файлов для визуального подтверждения всего, вы можете | head -20 по крайней мере посмотреть, выглядит ли оно грубо правильным, тогда как если вы просто rm my_pattern, у вас нет шансов обнаружить большую ошибку. – Nathan Long 2 December 2014 в 18:13
  • 4
    Вы можете найти файлы для показа, прежде чем вы их удаляете, оставьте вне -delete и просто используйте find . -type f ! -name "*.pdf" для печати на консоль, или на канал меньше или на файл. [а затем, при желании, переходите к xargs в rm как комментарии pabouk (с -print0 | ... -0 для странных имен файлов)] – Xen2050 3 December 2014 в 11:26

лучше ответить (по сравнению с моим предыдущим ответом) на этот вопрос будет с помощью мощной команды file.

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

теперь ваша проблема:

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

задание команды for дает файлы в текущем каталоге в виде переменной $var. Команда if-then выводит имена файлов в формате pdf, принимая статус выхода из 0 из команды file -i "$var" | grep -q 'application/pdf\;', она выдаст статус выхода 0 только в том случае, если находит файлы PDF.

2
ответ дан 24 May 2018 в 01:24
rm $(ls -lo|grep -v [Pp][Dd][Ff]$|awk '{print $7}')

Внимание! Лучше попробуйте сначала

ls -l $(ls -lo|grep -v [Pp][Dd][Ff]$|awk '{print $7}')
1
ответ дан 24 May 2018 в 01:24
rm -i -- !(*@(a|x).pdf)

Прочитайте как, удалите все файлы, которые не являются a.pdf или x.pdf.

Это работает, используя два расширенных шара, внешний !(), чтобы скрыть содержащиеся в нем glob который сам требует, чтобы glob должен соответствовать одному или нескольким шаблонам a или x до суффикса .pdf. См. 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
1
ответ дан 24 May 2018 в 01:24

Portable shell way

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

Довольно много POSIX и совместимо с любой оболочкой типа Bourne (ksh, bash, dash). Хорошо подходит для переносных скриптов и когда вы не можете использовать расширенную оболочку bash.

perl:

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

Или немного чище:

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

альтернативный питон

python -c 'import os;map(lambda x: os.remove(x), filter(lambda x: not x.endswith(".pdf"),os.listdir(".")))'
1
ответ дан 24 May 2018 в 01:24

Другие вопросы по тегам:

Похожие вопросы: