Вопрос или проблема
У меня есть каталог, содержащий большое количество файлов. Я хочу удалить все файлы, кроме 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
удобнее для простых задач. Всегда делайте бэкапы важных данных перед выполнением удаляющих команд, чтобы избежать потери информации.