Видаліть усі файли / каталоги, крім одного файлу


244

У мене є каталог, що містить велику кількість файлів. Я хочу видалити всі файли, крім file.txt. Як це зробити?

Занадто багато файлів для видалення непотрібних файлів окремо, а їх імена занадто різноманітні, щоб використовувати *, щоб видалити їх усіх, крім цього одного файлу.

Хтось запропонував використовувати

rm !(file.txt)

Але це не працює. Він повертає:

Badly placed ()'s 

Моя ОС - науковий Linux 6.

Будь-які ідеї?


13
перенести той, який ти хочеш зберегти, а потім передати інші?
Олів'є Дулак

5
@OlivierDulac Ми єдині двоє людей у ​​цьому питанні, які не замислюються над цим питанням?
Шадур

1
@shadur: ну, я можу відновитись до них: oneliners привабливі ... ^^
Олів'є Дулак

1
Дякую, і так, я шукав однолінійне рішення. Це занадто багато часу, щоб тримати файли, що рухаються, оскільки мені доводиться це робити досить часто.
Kantura

Відповіді:


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включений.


9
Вказання каталогу є хорошою практикою (fullpath в цьому випадку? Або, можливо, додати тут попередження про те, що ця команда видаляє кожен файл, починаючи з поточного робочого каталогу ?). Я також зазвичай пишу будь-який приклад, 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
Це не спрацює, якщо в іменах файлів буде пробіл ...
Маттео

1
Ciao @Matteo, він також працює для файлів з пробілами, але вам потрібно оточити греп-шаблон цитатами, наприклад 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)

Але для цього потрібна оболонка, яка підтримує тут-рядки.


10
Це дещо вражаюче.
vschum

2
@Derek - дивіться редагування.
mikeserv

2
Але якщо це перерветься на півдорозі, або якщо щось інше піде не так, файл зникне.
kasperd

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/

Зроблено.

EDIT Насправді, простіше:

cd ..
ln fulldir/file.txt ./
rm -rf fulldir
mkdir -p fulldir
mv file.txt fulldir/

3
це те саме, що робить моя відповідь. exce [pt, він не втрачає жодних дозволів на dir.
mikeserv

6
Якщо це великий файл у окремій файловій системі з / tmp, і ви намагаєтесь видалити все з кореня файлової системи вниз, то переміщення його кудись у безпеку може бути не можливим.
Джонні

1
Це, безумовно, найпростіша відповідь.
Бен Ліянаж

17

У моїй науковій ОС Linux 6 це працює:

shopt -s extglob
rm !(file.txt)

У мене також встановлений Debit 32bit на віртуальній машині. Зазначене не працює, але робиться наступне:

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

1
Ти маєш на увазі findрішення, яке я дав, не працювало?
cuonglm

"не працював" або "не працює". Так, ваше рішення пошуку також працює. Дякую.
Kantura

1
Чи можете ви детальніше? Як "не працювало? Він не видаляє інші файли, або його видалено file.txtчи щось інше?"
cuonglm

Я мав на увазі, що в ОС Debian "$ rm! (File.txt)" повертає "Неправильно розміщені () 's". Тому я спробував "$ shopt -s extglob", але він повернувся: "shopt: Command not found". Тоді я спробував рішення «знайти». Це справді працює.
Kantura

1
О, так, звичайно, це не працює. shoptне є tcshвбудованим.
cuonglm

10

Використовуйте rm !("file.txt")замістьrm !(file.txt)


4
Це абсолютно не має різниці. Проблема тут полягала в тому, що ОП був 1) не використовуючи оболонку, яка підтримує цей формат, і 2) навіть у bash, потрібно ввімкнути це shopt -s extglob. У будь-якому випадку, цитування такого простого імені файлу не мало би значення.
terdon

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

1

Я вважаю, що такий підхід дуже простий, працює і не вимагає особливих розширень (про що я знаю!)

ls --hide=file.txt | xargs rm
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.