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

244

У меня есть каталог, содержащий большое количество файлов. Я хочу удалить все файлы, кроме file.txt. Как мне это сделать?

Слишком много файлов, чтобы удалить ненужные по отдельности, а их имена слишком различны, чтобы использовать *, чтобы удалить их все, кроме этого одного файла.

Кто-то предложил использовать

rm !(file.txt)

Но это не работает. Возвращает:

Badly placed ()'s 

Моя ОС - Scientific Linux 6.

Есть идеи?

Kantura
источник
13
переместить тот, который вы хотите сохранить, а затем другие?
Оливье Дюлак
5
@OlivierDulac Являемся ли мы единственными людьми в этом вопросе, кто не задумывается над вопросом?
Шадур
1
@shadur: ну, я могу относиться к ним: симпатичные люди привлекательны ... ^^
Оливье Дюлак
1
Спасибо, и да, я искал однострочное решение. Перемещение файлов занимает слишком много времени, так как мне приходится делать это довольно часто.
Кантура

Ответы:

288

POSIXly:

find . ! -name 'file.txt' -type f -exec rm -f {} +

удалит все обычные файлы (за исключением рекурсивных, включая скрытые), кроме file.txt. Чтобы удалить каталоги, измените -type fна -type dи добавьте -rопцию к rm.

Для bashиспользования rm -- !(file.txt)необходимо включить extglob :

$ shopt -s extglob 
$ rm -- !(file.txt)

(или звонит bash -O extglob)

Обратите внимание, что extglobработает только в bashоболочке семейства Korn. И использование rm -- !(file.txt)может вызвать Argument list too longошибку.

В zsh, вы можете использовать ^для отмены шаблона с включенным extendedglob :

$ setopt extendedglob
$ rm -- ^file.txt

или используя тот же синтаксис , с kshи bashс опциями ksh_globи no_bare_glob_qualвключен.

cuonglm
источник
9
Указание каталога - хорошая практика (в данном случае полный путь? Или, может быть, добавьте предупреждение, что эта команда удаляет каждый файл, начиная с текущего рабочего каталога ?). Я также , как правило , писать пример с , rmкак echo rmвместо этого, и просить людей только вынимают эхо , когда они действительно уверены , что он будет делать то , что они хотят. Кроме этого, +1 за подробный ответ
Оливье Дюлак
11
Я бы посоветовал -deleteвместо -exec, короче и проще для запоминания
Изката
6
@Izkata: -deleteне определено POSIX.
Cuonglm
5
Как исключить список файлов?
Мейсам
5
@Meysam - см. Мой ответ для решения, которое будет обрабатывать список файлов. Еще с findрешением Gnouc вы можете сделать ! \( -name one_file -o -name two_file \)и так далее.
mikeserv
112

Еще один шаг в другом направлении (если в именах файлов нет пробелов)

ls | grep -v file.txt | xargs rm

или (работает, даже если в именах файлов есть пробелы)

ls | grep -v file.txt | parallel rm

от man grep:

 -v, --invert-match
          Invert the sense of matching, to select non-matching lines.  (-v is specified by POSIX)
Себастьян
источник
2
Это не будет работать, если в именах файлов есть пробел ...
Matteo
1
Ciao @Matteo, он работает и для файлов с пробелами, но вы должны заключить шаблон grep в кавычки, например ls | grep -v "a file with spaces.bin" | xargs rm. Это нормальный grepсинтаксис.
Себастьян
1
@Sebastian Проблема не в этом, grepно rm. rmполучит список разделенных пробелами аргументов. Попробуйте touch 'a b'; touch 'c d'; ls | grep -v 'a b' | xargs rm: вы получите rm: c: No such file or directoryиrm: d: No such file or directory
Маттео
1
Да, это общая проблема. Смотрите варианты -print0в findи -0в xargs. Вот обходной путь, но это немного неудобно. find . -maxdepth 1 -type f | grep -v 'a b' | tr '\n' '\0' | xargs -0 rm, Кстати, GNU parallelхорошо с этим справляется (я почти всегда использую его вместо xargs:ls | grep -v 'a b' | parallel rm
Себастьян,
1
Это не только пробелы, это все пробелы и символы новой строки, но также и символы в кавычках (одинарные, двойные кавычки и обратный слеш) и имена файлов, начинающиеся с -.
Стефан Шазелас
32

Сохраните копию, удалите все, восстановите копию:

{   rm -rf *
    tar -x
} <<TAR
$(tar -c $one_file)
TAR

В одну строку:

{ rm -rf *; tar -x; } <<< $(tar -c $one_file)

Но для этого требуется оболочка, которая поддерживает здесь-строки.

mikeserv
источник
10
Это несколько сногсшибательно.
Вщум
2
@Derek - пожалуйста, смотрите редактирование.
mikeserv
2
Но если это будет прервано на полпути, или если что-то пойдет не так, файл исчезнет.
Касперд
2
@kasperd - нет. Только если родительская оболочка умирает между временем запуска rmи tar -x. rmможет потерпеть неудачу столько, сколько хочет.
mikeserv
2
Разве не эффективнее переместить его в другой каталог и переместить обратно? Нам не нужно иметь дело с содержимым файла, только с его путем.
leonbloy
23

Вы все обдумываете это.

cd ..
mv fulldir/file.txt /tmp/
rm -rf fulldir
mkdir fulldir
mv /tmp/file.txt fulldir/

Готово.

РЕДАКТИРОВАТЬ На самом деле, проще:

cd ..
ln fulldir/file.txt ./
rm -rf fulldir
mkdir -p fulldir
mv file.txt fulldir/
Shadur
источник
3
это то же самое, что делает мой ответ. Exce [pt не теряет никаких разрешений в директории.
mikeserv
6
Если это большой файл в отдельной файловой системе из / tmp, и вы пытаетесь удалить все из корня файловой системы вниз, то его перемещение в безопасное место может оказаться невозможным.
Джонни
1
Это, безусловно, самый простой ответ.
Бен Лиянэйдж
17

На моей научной ОС Linux 6 это работает:

shopt -s extglob
rm !(file.txt)

У меня также установлен Debian 32bit на виртуальной машине. Выше не работает, но следующее работает:

find . -type f ! -name 'file.txt' -delete
Kantura
источник
1
Вы имеете в виду, что findрешение, которое я дал, не работает?
cuonglm
«не работает» или «не работает». Да, ваше решение для поиска также работает. Благодарю.
Кантура
1
Можете ли вы сделать это более подробно? Как «не работает? Он не удаляет другие файлы, или он удаляет, или что- file.txtнибудь еще?»
cuonglm
Я имел в виду, что в ОС Debian «$ rm! (File.txt)» возвращает «Плохо размещенные ()». Поэтому я попробовал «$ shopt -s extglob», но он вернул: «shopt: Команда не найдена». Тогда я попробовал «найти» решение. Это работает.
Кантура
1
О, так конечно, это не работает. shoptне является tcshвстроенным
cuonglm
10

Используйте rm !("file.txt")вместоrm !(file.txt)

www.wishinfo.net
источник
4
Это не имеет абсолютно никакого значения. Проблема заключалась в том, что OP был 1) без использования оболочки, поддерживающей этот формат, и 2) даже в bash, вам нужно включить его shopt -s extglob. В любом случае, цитирование простого имени файла не имело бы значения.
Тердон
1
Чтобы сохранить только папку с утилитой, выполните: - rm -rf! ("Utils")
Джеймс Джитин,
7

Просто чтобы дать другой ответ, вы можете использовать поведение по умолчанию rm, которое не удаляет папки:

mkdir tmp && mv file.txt tmp  # create tmp dir and move files there
rm                            # delete all other files
mv tmp/* . && rm -rf tmp      # move all files back and delete tmp dir
Nithin
источник
1

Я считаю, что этот подход очень прост, работает и не требует каких-либо специальных расширений (о которых я знаю!)

ls --hide=file.txt | xargs rm
2 бита
источник