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

Вопрос или проблема

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

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

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

rm !(file.txt)

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

Плохо расположенные ()'ы

Моя ОС — Scientific Linux 6.

Есть идеи?

Согласно POSIX (без -delete, -mindepth и т.д.):

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

удалит все обычные файлы (рекурсивно, включая скрытые), кроме файлов с именем file.txt.

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

В bash, чтобы использовать rm -- !(file.txt), вы должны включить extglob, который включает подмножество расширенных операторов глоба ksh, включая оператор отрицания !(...):

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

(или вызов bash -O extglob)

Также включите dotglob, чтобы удалять также скрытые файлы.

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

Использование -exec ... {} + в find обходит эту проблему, вызывая rm столько раз, сколько необходимо.

В ksh93 вы можете сделать то же самое с:

command -x rm -- !(file.txt)

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

$ setopt extendedglob
$ rm -- ^file.txt

или с использованием того же синтаксиса с ksh и bash с включенными опциями ksh_glob и no_bare_glob_qual (или emulate ksh для еще более близкой эмуляции поведения ksh).

Чтобы обойти проблему слишком много аргументов, вы можете использовать его автозагружаемую функцию zargs.

Чтобы удалить скрытые файлы, добавьте глоб-квалификатор D (или включите dotglob, как в bash, но это повлияет на все глоб-выражения, как в bash). Чтобы удалить только обычные файлы, добавьте квалификатор .. И используйте **/ для рекурсивного глобирования. Таким образом, эквивалент первой команды find будет выглядеть так:

autoload zargs
zargs ./**/*(D.oN) -- rm -f

(добавляя oN, чтобы пропустить сортировку для еще более точного совпадения).

Другой подход в другом направлении (если в именах файлов нет пробелов, табуляций, переносов строк, одинарных кавычек, двойных кавычек или символов обратного слэша¹), используя -f, чтобы избежать запросов, на которые нельзя ответить, так как ввод поступает либо из канала, либо из /dev/null в зависимости от реализации xargs:

ls | grep -xvF file.txt | xargs rm -f --

или с помощью GNU parallel (не parallel из moreutils), что приводит к проблемам с меньшим количеством символов², но означает выполнение одного sh и одного rm для каждого файла:

ls | grep -xvF file.txt | PARALLEL_SHELL=sh parallel rm -f --

Для произвольных имен файлов, на современных системах GNU и с оболочкой, поддерживающей подстановку процессов в стиле Korn, при этом ввод rm остается неизменным:

xargs -r0a <(ls --zero | grep -zxvF file.txt) rm --

Хотя мы также можем обойтись без grep, так как GNU ls имеет опцию --ignore для пропуска записей, соответствующих шаблону:

xargs -r0a <(ls --zero --ignore=file.txt) rm --

из man grep на системе GNU:

 -v, --invert-match
          Инвертировать значения соответствия, чтобы выбрать несовпадающие строки.

 -F, --fixed-strings
          Интерпретировать ШАБЛОНЫ как фиксированные строки, а не регулярные выражения.

 -x, --line-regexp
          Выберите только те совпадения, которые точно совпадают с
          всей строкой. Для регулярного выражения это
          похоже на то, чтобы взять шаблон в скобки
          и окружить его ^ и $.

 -z, --null-data
          Обрабатывайте входные и выходные данные как последовательности строк, каждая из которых
          завершается нулевым байтом (ASCII NUL символом) вместо
          нового переноса. Эта опция, как -Z или --null, может
          быть использована с командами, такими как sort -z для обработки произвольных имен
          файлов.

(-x, -v, -F являются стандартными, остальное, включая длинные формы стандартных опций, является расширениями GNU).

Без -x мы также сохранили бы my-file.txt, а без -F мы также сохранили бы file-txt.


¹ и с некоторыми реализациями xargs, возможно, другие пробелы, классифицируемые как таковые в локали, и без файла с именем _ и без файла, который не может быть декодирован как текст в локали.

² Обычно только переносы строк, но более старые версии GNU parallel не всегда правильно экранировали все строки перед передачей в оболочку, поэтому YMMV; мы смягчаем это здесь, заставляя оболочку быть sh, с которой экранирование проще и синтаксис хорошо известен.

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

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

В одной строке:

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

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

Вы все это переоцениваете.

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/

На моей ОС Scientific Linux 6 это работает:

shopt -s extglob
rm !(file.txt)

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

find . -type f ! -name 'file.txt' -delete

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

ls --hide=file.txt | xargs -d '\n' rm

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

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

mkdir tmp && mv file.txt tmp  # создайте каталог tmp и переместите файлы в него
rm                            # удалите все другие файлы
mv tmp/* . && rm -rf tmp      # переместите все файлы обратно и удалите каталог tmp

В моем случае мне нужно было удалить все файлы и папки, кроме zip-файлов, вдохновленный принятым ответом (я, конечно, поставил +1), в то время как из папки, которую я хочу очистить, все кроме zip-файлов, я использую find . ! -name '*.zip' ! -name '.' ! -name '..' -exec rm -rf {} +, например (все файлы/папки пусты, так как созданы только для примера):

my-computer:/tmp/toclean>ll
total 0
-rw-r--r-- 1 user users 0 Oct  4 10:47 aaa.zip
-rw-r--r-- 1 user users 0 Oct  4 10:47 tata
drwxr-xr-x 1 user users 0 Oct  4 10:47 titi
drwxr-xr-x 1 user users 0 Oct  4 10:47 tutu
-rw-r--r-- 1 user users 0 Oct  4 10:47 yoyo
-rw-r--r-- 1 user users 0 Oct  4 10:47 zzz.zip
my-computer:/tmp/toclean>find . ! -name '*.zip' ! -name '.' ! -name '..'  -exec rm -rf {} +
my-computer:/tmp/toclean>ll
total 0
-rw-r--r-- 1 user users 0 Oct  4 10:47 aaa.zip
-rw-r--r-- 1 user users 0 Oct  4 10:47 zzz.zip

Обратите внимание, что я исключаю '.' и '..', чтобы избежать сообщения об ошибке rm: отказываюсь удалять '.' или '..' директорию: пропускаю '.'.

Просто.

mkdir -p empty_dir && rsync -av --exclude 'file.txt' --exclude 'folder/' --delete empty_dir/ ./

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

Убедитесь, что вы добавили косую черту в конце для папок, так как это важно для rsync.

Ответ или решение

Для удаления всех файлов и директорий в каталоге, кроме одного конкретного файла, например file.txt, можно воспользоваться несколькими подходами в зависимости от ваших требований и особенностей операционной системы. Здесь мы рассмотрим наиболее оптимальные и безопасные методы для выполнения этой задачи на операционной системе Scientific Linux 6.

Подход 1: Использование команды find

Команда find – один из самых популярных инструментов для выполнения операций с файлами и каталогами. Чтобы удалить все файлы, кроме file.txt, можно использовать следующую команду:

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

Эта команда ищет все файлы, которые не называются file.txt, и удаляет их. Она рекурсивно обходит все подкаталоги и обрабатывает найденные файлы в пакетном режиме, что минимизирует вероятность ошибки "Слишком много аргументов".

Если также необходимо удалить каталоги, используйте команду:

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

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

Подход 2: Использование расширенной настройки оболочки bash

Если вы хотите использовать возможности командной оболочки bash, можно активировать функцию extglob, что позволит использовать шаблоны для более гибкого выбора файлов:

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

Если необходимо также удалить скрытые файлы, включите опцию dotglob:

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

Подход 3: Использование xargs

Если вы не хотите включать расширенные функции оболочки, можно использовать комбинацию с командой xargs. Таким образом, можно избежать проблем с делением на аргументы:

ls | grep -v 'file.txt' | xargs rm -f --

Однако будьте осторожны: этот метод может не сработать корректно, если имена файлов содержат пробелы или другие специальные символы. Используйте null символы для обработки имен файлов:

ls -1Z | grep -vZ 'file.txt' | xargs -0 rm -f --

Подход 4: Перемещение и восстановление файла

Если вам нужно просто избавиться от всех файлов и оставить только один, можно переместить file.txt временно в другой каталог, удалить все, а затем вернуть файл:

mv file.txt /tmp/
rm -rf *
mv /tmp/file.txt ./

Этот способ также эффективен и прост в реализации.

Подход 5: Использование rsync

Еще один элегантный способ – воспользоваться rsync, чтобы синхронизировать содержимое с пустой директорией:

mkdir -p empty_dir
rsync -av --exclude='file.txt' --delete empty_dir/ ./

Этот подход гарантирует, что все файлы, кроме file.txt, будут удалены, и требует минимальных ресурсов.

Заключение

Каждый из предложенных методов имеет свои преимущества и недостатки. Выбор подхода зависит от ваших предпочтений, наличия необходимых инструментов и особенностей рабочего окружения. Команда find наилучшим образом подходит для обработки большого числа файлов и директорий, тогда как использование rm с активированной extglob удобнее для простых задач. Всегда делайте бэкапы важных данных перед выполнением удаляющих команд, чтобы избежать потери информации.

Оцените материал
Добавить комментарий

Капча загружается...