Видаліть усі файли в каталозі, ім'я якого не відповідає рядку в списку файлів


9

У мене каталог з 1000+ файлами. У текстовому файлі я маю близько 50 імен файлів, по одному на рядок. Я хотів би видалити всі файли в каталозі, імена файлів яких не відповідають запису в списку. Який найкращий спосіб зробити це? Я запустив скрипт оболонки, але не зміг визначити належну команду для визначення ім'я файлу в списку. Дякую.

Відповіді:


8

Я усвідомлюю, що будь-яке запитання щодо видалення файлів потрібно сприймати дуже обережно. Моя перша відповідь була надто поспішною, я не сприймав факту, що список файлів може бути неправильно використаний для використання з програмою egrep. Я змінив відповідь, щоб зменшити цей ризик.

Це має працювати для файлів, у яких немає місця в назві:

Спочатку відновіть свій список файлів, щоб переконатися, що він відповідає точному імені файлу:

sed -e 's,^,^,' -e 's,$,$,'  filelist  > newfilelist 

побудувати команди rm

cd your_directory
ls | egrep -vf newfilelist   | xargs -n 1 echo rm  >  rmscript

Перевірте, чи підходить вам сценарій rm (ви можете це зробити з "vim" або "менше").
Потім виконайте дію:

sh -x rmscript

Якщо у файлах є пробіли у своєму імені (якщо файли мають "ім'я, то це не буде працювати):

ls | egrep -vf newfilelist  | sed 's,^\(.*\)$,rm "\1",' > rmscript

звичайно, список файлів не повинен знаходитися в одному каталозі!

ВІДМОВЛЕНО:

Список файлів Натана містив імена, які відповідали всім файлам у каталозі (наприклад, "html" відповідає "bob.html"). Тож нічого не було видалено, оскільки egrep -vfпоглинув увесь потік. Я додав команду поставити "^" і "$" навколо кожного імені файлу. Мені пощастило, що список файлів Натана був правильним. Якби DOS було відформатовано за допомогою рядків, що закінчуються CR-LF, або з додатковими пробілами, жодні файли не збереглися за допомогою egrep і всі були видалені.


Коли я запускаю команду попереднього перегляду, я отримую один рядок з "rm". Коли я запускаю фактичну команду, я отримую повідомлення про помилку щодо відсутніх аргументів для rm. Чи потрібен спеціальний синтаксис, щоб використовувати результати з ls | egrep у вході xargs?
Натан

@Nathan, ви повинні спочатку перейти до свого каталогу. Немає спеціального синтаксису. lsнадає назви файлів каталогів, egrep -vf filelistфільтрує 50 імен файлів. Боюся, ви видалили всі свої файли.
Еммануель

@Emamanuel Я запускаю команду з каталогу, який містить файли, які потрібно видалити.
Натан

@Nathan чи всі ваші файли видалені?
Еммануель

ні, вони все ще там.
Натан

1

Попередньо побудуйте аргументи для find:

{
  read -r
  keep=( -name "$REPLY" ) # no `-o` before the first one.
  while read -r; do
    keep+=( -o -name "$REPLY" )
  done
} < file_list.txt
find . -type f ! \( "${keep[@]}" \) -exec echo rm {} +

Використовуйте echoдеталі, щоб побачити, що буде побудовано. Видаліть echoдеталі, щоб фактично її запустити.

Оновлення: Демонстрація:

##
# Demonstrate what files exist for testing.
# Show their whitespace:
~/foo $ printf '"%s"\n' *
" op"
" qr"
"abc"
"def"
"gh "
"ij "
"k l"
"keep"
"m n"

##
# Show the contents of the "keep" file,
# Including its whitespace:
~/foo $ cat -e keep
keep$
abc$
gh $
k l$
 op$

##
# Execute the script:
~/foo $ { read -r; keep=( -name "$REPLY" ); while read -r ; do keep+=( -o -name "$REPLY" ); done } < keep
~/foo $ find . -type f ! \( "${keep[@]}" \) -exec rm {} +

##
# Show what files remain:
~/foo $ printf '"%s"\n' *
" op"
"abc"
"gh "
"k l"
"keep"

Мені це подобається найкраще, оскільки він видаляє список потрібних файлів
eyoung100

+1 від мене, хоча це не дуже добре справляється з просторами. Можливо, 'слід додати якісь окремі цитати ( ), тобто keep=( -name \'"$REPLY"\' )і keep+=( -o -name \'"$REPLY"\' ).
Cristian Ciupitu

вищесказане небезпечно, оскільки ви можете видалити файли випадково.
davidva

@CristianCiupitu чи не так? Я додав демонстрацію, яка показує, що вона дуже добре справляється з пробілом.
kojiro

@davidva За яких обставин? Кожен раз, коли ви автоматизуєте видалення речей, ви ризикуєте помилитися, але в межах параметрів питання, я думаю, моя демонстрація доводить, що цей підхід є здоровим.
якийro

1

З zsh:

mylist=(${(f)"$(<filelist)"})
print -rl -- *(.^e_'(($mylist[(Ie)$REPLY]))'_)

Він зчитує рядки filelistв масиві, а потім використовує глоффіліфікатори / estring для glob / вибирає лише імена файлів, які відсутні в масиві: .вибирає лише звичайні файли (додайте, Dякщо ваш список містить точкові файли), а заперечне ^e_'expression'_додатково вибирає лише ті для який вираз повертає помилково, тобто якщо їх ім'я ( $REPLY) не є елементом масиву .
Якщо ви задоволені результатом заміни print -rlз rmфактично видалити файли:

rm -- *(.^e_'(($mylist[(Ie)$REPLY]))'_)

Для вибору та видалення файлів рекурсивно використовуйте */**глобус із ${REPLY:t}модифікатором glob:

rm -- */**(.^e_'(($mylist[(Ie)${REPLY:t}]))'_)

0

Якщо ви помістите вміст каталогу у такий файл:

cd <somedirectory>
ls >> filelist

Відкрийте список файлів за допомогою текстового редактора та видаліть усі файли, окрім тих, які ВИ ХОЧЕТЕ ВИДАЛИТИ . Це жирним шрифтом, оскільки це протилежний підхід до відповіді вище

Спробуйте це:

while read p || [[ -n $p ]]; 
echo $p
done < filelist

Якщо ви бачите свій список файлів, що виводяться на екран, замініть відлуння rm -vтаким чином:

while read p || [[ -n $p ]]; 
rm -v $p
done < filelist

0

Запустіть сценарій нижче.

  1. Спочатку я знаходжу всі файли, які є всередині каталогу, і зберігаю вихід в інший файл all_files.
  2. У нас є файл зі списком файлів, які НЕ слід видаляти ( not_to_be_deleted_files).
  3. Я додаю назви файлів not_to_be_deleted_filesі files_to_be_deletedв кінці, not_to_be_deleted_filesяк нам потрібні ці 2 файли.
  4. Тепер я знаходжу файли, які потрібно видалити за допомогою joinкоманди Linux та перенаправляти вихід у files_to_be_deleted файл.
  5. Тепер, під час остаточного циклу, я читаю всі назви файлів у files_to_be_deletedта видаляю файли, згадані в цьому імені файлу.

Сценарій наведений нижче.

find /home/username/directory -type f | sed 's/.*\///' > all_files
echo all_files >> not_to_be_deleted_files
echo not_to_be_deleted_files >> not_to_be_deleted_files
echo files_to_be_deleted >> not_to_be_deleted_files
join -v 1 <(sort all_files_listed) <(sort files_not_to_be_deleted) >   files_to_be_deleted
while read file
rm  "$file"
done < files_to_be_deleted

PS : Мабуть, якщо ви хочете, щоб це було збережено як сценарій і запустіть його, ви можете також додати ім'я сценарію, використовуючи echo scriptname >> not_to_be_deleted_files.

Хоча це і не потрібно, я вважаю за краще це робити, тому що не буде жалю згодом. Я перевірив наявність невеликого набору файлів, і він працював у моїй системі. Однак якщо ви хочете бути впевненими, спробуйте testспочатку в каталозі, а потім видаліть файли в оригінальному каталозі.


0
  • Використовуйте список як джерело, щоб перемістити всі файли у списку до нового, нового та порожнього збереження.
  • Порівняйте кількість файлів у списку та кількість збережених файлів.
  • Якщо обидва збігаються, видаліть усі збережені файли улюбленим способом.
  • Поверніть збережені файли назад.

0

Я пішов на більш безпечний і набагато швидший підхід, оскільки в списку було 18 000 файлів! Мені потрібно було очистити зображення у великій інсталяції Drupal.

Видалення всіх файлів у списку - це те саме, що зберігати лише ті, які є у списку. Тому я вирішив фактично скопіювати файли зі списку в інше місце, але копіювання 20 ГБ файлів зайняло б занадто багато місця, а також дуже повільне. Тож хитрість полягає в тому, щоб скопіювати файли як hardlinksзамість цього, скориставшись -lопцією cp. Це займає майже не місце і дуже швидко. Крім того, оскільки мені потрібно було зберегти структуру каталогу, я використав цей --parentsваріант.

Ось уривок зі мого списку файлів:

1px.png
misc/feed.png
modules/file/icons/x-office-presentation.png
modules/file/icons/x-office-spreadsheet.png
newsletter.png
sites/all/libraries/ckeditor/plugins/smiley/images/devil_smile.png
sites/all/libraries/ckeditor/plugins/smiley/images/regular_smile.png
sites/default/files/009313_PwC_banner_CBS_Observer_180x246px.jpg

Таким прикладом може бути рядок із тим, що призначенням є temp:

cp -l --parents 'misc/feed.png' temp

Це створить цю структуру:

temp
  misc
    feed.png

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

Наступним кроком є ​​побудова сценарію:

sed -e "s,^,cp -l --parents '," -e "s,$,' /some/where/temp," filelist > newfilelist

Тепер, припускаючи, що ви вже створили порожній dir / some / where / temp, ви можете скопіювати такі файли:

sh newfilelist 2> missing_files

Зверніть увагу, як з'являються помилки в missing_files. Додатковим бонусом такого підходу є те, що ви отримаєте список файлів з оригінального списку, які насправді не існують!

Після запуску сценарію, temp міститиме лише ті файли, які є у списку файлів, але нічого не видаляючи та не займаючи додаткового місця. Якщо ви задоволені результатом, ви можете видалити всі початкові файли, включаючи підпапки.

Нарешті, перемістіть файли та папки з темп назад до початкового місця.

На 18 000 файлів знадобилося лише кілька секунд.


0

Безпечний, простий.

cd до каталогу.

Створіть тимчасовий каталог.

mv *.yourExlusionSelector.* ./temp
rm *
mv ./temp ./
rm -rf ./temp

зроблено.


Ласкаво просимо на сайт. Хоча ваш підхід спрацює, якщо імена зі списку, згаданого ОП, є результатом простого узгодження шаблону - що, можливо, так і є, - зауважте, що ОП заявила, що назви файлів, які потрібно виключити, зберігаються у певному файлі; ви можете розширити свою відповідь, щоб прочитати шаблони виключення з цього файлу, а не покладатися на один статичний візерунок або набрати копіювати потенційно кілька шаблонів на консоль.
AdminBee
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.