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

50

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

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

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

помешанный
источник

Ответы:

63
cd <the directory you want>
find . -type f ! -iname "*.pdf" -delete
  • Первая команда приведет вас в каталог, в котором вы хотите удалить свои файлы
  • Вторая команда удалит все файлы, кроме тех, которые заканчиваются .pdfна имя файла

Например, если tempв вашей домашней папке есть каталог:

cd ~/temp

затем удалите файлы:

find . -type f ! -iname "*.pdf" -delete

Это удалит все файлы, кроме xyz.pdf.

Вы можете объединить эти две команды для:

find ~/temp -type f ! -iname "*.pdf" -delete

.это текущий каталог. !означает взять все файлы, кроме тех, которые .pdfв конце. -type fвыбирает только файлы, а не каталоги. -deleteзначит удалить это.

ПРИМЕЧАНИЕ: эта команда удалит все файлы (кроме файлов pdf, но включая скрытые файлы) в текущем каталоге, а также во всех подкаталогах. !должен прийти раньше -name. просто -nameвключит только .pdf, пока -inameвключит .pdfи.PDF

Чтобы удалить только в текущем каталоге, а не в подкаталогах, добавьте -maxdepth 1:

find . -maxdepth 1 -type f ! -iname "*.pdf" -delete
Эдвард Торвальдс
источник
Спасибо за ответ. Можете ли вы помочь мне немного понять синтаксис? .значит "а"? !означает «кроме» -nameозначает, что вы хотите исключить с помощью параметра имени, а затем какое -deleteдействие предпринять при поиске? Так он ищет все, кроме "* .pdf" и удаляет их? Или я неправильно понял?
jessenorton
.означает текущий каталог. !означает взять все файлы, кроме того, что .pdfв конце. -deleteзначит удалить это. я теперь ясно?
Эдвард Торвальдс
@terdon Starkers сказал, что нет подкаталогов. Будем плохо редактировать мой ответ, чтобы быть более широким
Эдвард Торвальдс
+1 Вы должны были включить -maxdepth 1параметр для начала. Затем предложите удалить параметр в случае, если вы хотите удалить рекурсивно.
Тулаинс Кордова
3
это привлекло мое внимание к тому, что мы должны использовать -inameвместо них -name, иначе .PDFбудут проскакивать файлы с расширением.
Муру
43

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

rm -- *.!(pdf)

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

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

rm -- !(*.pdf)

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

shopt -s extglob

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

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

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

steeldriver
источник
Лучше подход ИМО.
Таккат
А как насчет файлов без расширения? FWIW, в Zsh этоrm *~*.pdf
Эмиль Jeřábek
1
Я бы поставил точку в скобках.
Деннис Уильямсон
4
Ах, звездочка тоже должна идти внутрь !(*.py). Также, предположительно, если OP хочет, чтобы оставались только файлы «.pdf», то файлы без расширений также должны быть удалены и не игнорироваться.
Деннис Уильямсон
1
Этот подход проще и аккуратнее, чем принятый ответ.
Питер
18

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

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

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

mv !(*.pdf) ~/.local/share/Trash/files
αғsнιη
источник
6
Такой подход намного безопаснее, чем непосредственное использование rm.
Сет
14

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

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

Джерри
источник
4
+1 Еще раз за комментарий, который имеет смысл для начинающего пользователя, который почти наверняка не приведет к непреднамеренному удалению файлов.
trognanders
4

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

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

Со страницы руководства bash:

GLOBIGNORE:

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

Быстрый тест:

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
Кир
источник
3

Будьте осторожны и составьте: используйте Xargs

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

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

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

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

Обновить

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

Натан Лонг
источник
4
Я заметил три проблемы: а) он исключит любой файл, содержащий pdfгде-либо его имя. --- b) Он удалит файлы PDF, если любая из букв в суффиксе будет прописной. --- в) Не рекомендуется использовать вывод ls. Он не будет работать с именами файлов, содержащими переводы строк. Некоторые реализации lsзаменяют специальные символы, например tab ?. --- Лучше использования: find -maxdepth 1 -print0. (не так коротко, как ls:) ----- Для разрешения а) и б) используйте grep -vi '\.pdf$'--- полное (но только для GNU) решение:find -maxdepth 1 -print0 | grep -viz '\.pdf$' | xargs -0 rm
pabouk
1
Я понимаю, что вы имели в виду решение как "интерактивный" процесс с несколькими ручными итерациями, но проверки вряд ли пригодятся для длинных списков файлов, и проблемы, упомянутые выше, могут легко упускать из виду ошибки.
Пабук
1
@pabouk хорошие очки; реальный мир всегда усложняет вещи, и ваши исправления полезны. :) Но я все еще думаю, что этот общий подход лучше. Если файлов слишком много, чтобы все визуально подтвердить, вы можете | head -20хотя бы посмотреть, выглядит ли это примерно правильно, а если вы просто rm my_pattern, у вас нет шансов обнаружить большую ошибку.
Натан Лонг
1
Вы можете найти, покажет вам файлы, прежде чем удалять их, не указывать опцию -delete и просто использовать ее find . -type f ! -name "*.pdf"для печати на консоль, или для передачи на less или в файл. [и затем направьте xargs к rm, если хотите, как комментарии Пабука (с -print0 | ... -0 для странных имен файлов)]
Xen2050
3

Обычно я решаю такие проблемы с помощью интерактивного интерпретатора Python:

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

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

мышей
источник
Для тех, кто все больше нервничает с каждой дополнительной строкой, мы могли бы превратить ее в одну:for item in [f for f in os.listdir('.') if not f.endswith('.pdf')]: os.remove(item)
Джейкоб Влейм
python -c "import os; for f in os.listdir('.'): if not f.endswith('.pdf'): os.remove(f)"
mic_e
[os.remove(f) for f in os.listdir('.') if not f.endswith('.pdf')]
mic_e
отлично! второй дает мне синтаксическую ошибку, не понимаю почему.
Джейкоб Влейм
странный; он работает с Python 3.4 и Python 2.7 в моей системе.
mic_e
2

Лучшим ответом (по сравнению с моим предыдущим ответом) на этот вопрос будет использование мощной 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, принимая статус выхода команды 0from file -i "$var" | grep -q 'application/pdf\;', она выдаст статус выхода, 0только если найдет файлы pdf.

Эдвард Торвальдс
источник
1
rm $(ls -lo|grep -v [Pp][Dd][Ff]$|awk '{print $7}')

Предупреждение! Лучше попробуй первым

ls -l $(ls -lo|grep -v [Pp][Dd][Ff]$|awk '{print $7}')
Мартин-Блас Перес Пинилья
источник
2
Тьфу, в этом есть множество недостатков: smalllo.ruhr.de/award.html#ls , smalllo.ruhr.de/award.html#grep , и он полностью игнорирует имена файлов с пробелами или специальными символами.
Дэвид Фёрстер
1
Вы должны действительно использовать -iс grepдля сопоставления без учета регистра.
Муру
1
rm -i -- !(*@(a|x).pdf)

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

Это работает, используя 2 расширенных глоба, внешний !()для отмены содержащегося глобуса, который сам по себе требует, чтобы шар соответствовал одному или нескольким из 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
shalomb
источник
1

портативный способ оболочки

$ 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(".")))'
Сергей Колодяжный
источник
0

Будьте осторожны с тем, что вы удаляете!

Безопасный способ проверить его перед попыткой удаления - это сначала выполнить тестирование ls, поскольку некоторые необработанные действия могут удалить ненужные файлы. И вы можете сделать это прямо за пределами каталога. lsпохоже rm, так:

ls sub/path/to/files/!(*.pdf)

Это будет список

y.zip
z.mp3

И теперь вы можете видеть, что вы удаляете и можете безопасно удалить их:

rm sub/path/to/files/!(*.pdf)

Вот и все. Йо может использовать подстановочный знак, *чтобы быть более избирательным, например, хранить только документы курса:

rm sub/path/to/files/!(*programming*)
KeitelDOG
источник