видаліть файл, але виключіть усі файли зі списку


17

Мені потрібно періодично чистити папку. Я отримую список файлів, який містить текст, які файли дозволені. Тепер я маю видалити всі файли, яких немає у цьому файлі.

Приклад:

dont-delete.txt:

dontdeletethisfile.txt
reallyimportantfile.txt
neverdeletethis.txt
important.txt

Очищення моєї папки містить це як приклад:

ls /home/me/myfolder2tocleanup/:

dontdeletethisfile.txt
reallyimportantfile.txt
neverdeletethis.txt
important.txt
this-can-be-deleted.txt
also-waste.txt
never-used-it.txt

Отже, ці файли слід видалити:

this-can-be-deleted.txt
also-waste.txt
never-used-it.txt

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


Це домашнє завдання?
mook765

Я сподіваюся, ти не його вчитель. lol
Гуджарат Сантана

2
@gujarat Ми не є безкоштовним домашнім завданням, тому коментар виправданий. Що стосується самого питання, воно може бути корисним для інших, тому воно поки що відкрите.
Сергій Колодяжний

@Serg Я повністю з вами згоден
Гуджарат Сантана

Відповіді:


9

rmКоманда закоментований , так що ви можете перевірити і переконатися , що він працює по мірі необхідності. Потім просто не коментуйте цей рядок.

Цей check directoryрозділ гарантує, що ви випадково не запустите скрипт із неправильної директорії та не зафіксуєте неправильні файли.

Ви можете видалити echo deletingлінію, щоб запустити мовчки.

#!/bin/bash

cd /home/me/myfolder2tocleanup/

# Exit if the directory isn't found.
if (($?>0)); then
    echo "Can't find work dir... exiting"
    exit
fi

for i in *; do
    if ! grep -qxFe "$i" filelist.txt; then
        echo "Deleting: $i"
        # the next line is commented out.  Test it.  Then uncomment to removed the files
        # rm "$i"
    fi
done

Я відредагував ваш код, щоб уникнути марного використанняls та марного фіксації результатів, grepякщо все, що ви хочете знати, - це збіг чи ні. Я також використовував шаблони з фіксованим рядком, щоб уникнути проблем.
Девід Фоерстер

@DavidFoerster Дякую за внесок. Однак, коли ви змінили whileцикл в forциклі ви випадково змінили iteration keyвід iдо f. в декларації, яка порушила код. Я полагодив це.
Л. Д. Джеймс

Ой, сила звички. Я схильний скорочувати назви змінних оболонок для імен файлів як f. ;-P (… і +1 за вашу відповідь, яку я забув раніше.)
Девід Фоерстер

10

Цей сценарій python може це зробити:

#!/usr/bin/env python3
import os
no_remove = set()
with open('./dont-delete.txt') as f:
     for line in f:
         no_remove.add(line.strip())

for f in os.listdir('.'):
    if f not in no_remove:
        print('unlink:' + f ) 
        #os.unlink(f)

Важлива частина - це відміняти os.unlink()функцію.

ПРИМІТКА : додайте цей скрипт і dont-delete.txtдо свого, dont-delete.txtщоб вони обидва були у списку, і зберігайте їх у одному каталозі.


1
Я змінив ваш код, щоб використовувати setзамість списку для O (1) замість пошуку O (n) у другій частині.
Девід Фоерстер

дякую за допомогу, я зазвичай хлопець з Windows, але пітони
шва

1
@ stefan83: Python працює так само добре в Windows.
Девід Фоерстер

3

Ось один вкладиш:

comm -2 -3 <(ls) <(sort dont_delete) | tail +2 | xargs -p rm
  1. ls друкує всі файли в поточному каталозі (в упорядкованому порядку)
  2. sort dont_delete друкує всі відтворені файли в упорядкованому порядку
  3. <()оператор перетворює рядок у файл-подібний об'єкт
  4. Ці commкоманди порівнюють два попередньо відсортовані файли і друкує лінію , на яких вони відрізняються
  5. використання -2 -3прапорів спричиняє commдрукування лише рядків, що містяться в першому файлі, але не у другому, який буде списком файлів, які безпечно видалити
  6. tail +2виклик просто видалити заголовок commвиведення, який містить ім'я вхідного файлу
  7. Тепер ми отримуємо список файлів, які потрібно видалити стандартно. Ми передаємо цей вихід, до xargsякого перетворять вихідний потік у список аргументів rm. В -pопції сили xargsпопросити підтвердження перед виконанням.

THX для вашої допомоги, тепер у мене є рішення!
stefan83

@gardenhead, я втомив ваш код, але він видаляє всі файли в каталозі і зберігає лише перший і останній файл у списку dont-delete. у вас є ідеї щодо цієї проблеми? Заздалегідь спасибі.
Negar

1

FWIW, схоже, ви можете зробити це вдома zsh, використовуючи (+cmd)глобальний класифікатор.

Для ілюстрації почнемо з деяких файлів

 % ls
bar  baz  bazfoo  keepfiles.txt  foo  kazoo

і файл білого списку

 % cat keepfiles.txt
foo
kazoo
bar

Спочатку прочитайте білий список у масив:

 % keepfiles=( "${(f)$(< keepfiles.txt)}" )

а може, і краще

 % zmodload zsh/mapfile
 % keepfiles=( ${(f)mapfile[./keepfiles.txt]} )

(еквівалент mapfileвбудованого bash - або його синоніма readarray). Тепер ми можемо перевірити, чи існує ключ (ім’я файлу) в масиві, використовуючи ${keepfiles[(I)filename]}який повертає 0, якщо не знайдено відповідності:

 % print ${keepfiles[(I)foo]}
1
 % print ${keepfiles[(I)baz]}
0
 %

Ми можемо скористатися цією функцією, яка повертається, trueякщо $REPLYв масиві немає відповідностей :

% nokeep() { (( ${keepfiles[(I)$REPLY]} == 0 )); }

Нарешті, ми використовуємо цю функцію як класифікатор у нашій команді:

 % ls *(+nokeep)
baz  bazfoo  keepfiles.txt

або, у вашому випадку

 % rm -- *(+nokeep)

(Ви, ймовірно, захочете додати ім’я файлу білого списку до білого списку.)


0

Якщо припустити, що ваша оболонка bash extglob shoptувімкнута, ось дещо консервативніша альтернатива:

rm !($(tr \\n \| < keep.txt))

(... супроводжує інакше відмінна пропозиція від @ gardenhead!)


0

Якщо виведення не ls /home/me/myfolder2tocleanup/перевищує максимальну межу аргументу оболонки, ARG_MAX яка становить близько 2 Мб для Ubuntu, я б запропонував наступне.


Реалізація команд у одному рядку, яка виконає цю роботу, буде такою:

  1. Скопіюйте dont-delete.txtфайл у каталог, що містить файли, які слід видалити так:
cp dont-delete.txt /home/me/myfolder2tocleanup/
  1. cd до каталогу, що містить файли, які потрібно видалити так:
cd /home/me/myfolder2tocleanup/
  1. Зробіть сухий запуск, щоб протестувати команду та змусити її друкувати імена файлів, які вона виявляє, як видалити, не фактично видаляючи їх, як-от так:
ls -p | grep -v / | sed 's/\<dont-delete.txt\>//g' | sort | comm -3 - <(sort dont-delete.txt) | xargs echo | tr " " "\n"
  1. Якщо ви задоволені результатом, видаліть файли, виконавши команду так:
ls -p | grep -v / | sed 's/\<dont-delete.txt\>//g' | sort | comm -3 - <(sort dont-delete.txt) | xargs rm

Пояснення:

  • ls -pбуде перераховано всі файли та каталоги в поточному каталозі, а опція -pдодасть /до імен каталогу.
  • grep -v /буде виключено каталоги, видаливши всі елементи, що містять /у своїх назвах.
  • sed 's/\<dont-delete.txt\>//g'буде виключати dont-delete.txtфайл, тому він не видаляється в процесі.
  • sortбуде, щоб переконатися, сортувати решту вихідних даних ls.
  • comm -3 - <(sort dont-delete.txt)буде сортувати dont-delete.txtфайл, порівнювати його з відсортованим результатом lsта виключати назви файлів, які існують в обох.
  • xargs rmвидалить усі залишилися імена файлів у вже обробленому виході ls. Це означає, що всі елементи з поточного каталогу будуть видалені, крім каталогів , файлів, перелічених у dont-delete.txtфайлі, та самого dont-delete.txtфайлу

У сухій частині:

  • xargs echo надрукує файли, які слід видалити.
  • tr " " "\n" переведе пробіли в нові рядки для легшої читабельності.

0

Я настійно пропоную використовувати rsyncрозміщений тут розчин ; в іншому випадку використовуйте розчин, вказаний нижче.

Якщо припустити, що у ваших файлах немає пробілу (Пробіли / вкладки), вказаного у файлі, який називається excludelist, тоді ви зробите:

find /path/to -type f \( ! -name "excludelist" $(printf ' -a ! -name %s\n' $(< excludelist)) \)

Просто додайте -deleteдо команди вище, щоб видалити файли, яких немає у файлі виключення . Якщо знахідка не має -deleteопцію можна використовувати rmз -execнаступним чином :

find /path/to -type f \( ! -name "excludelist" $(printf ' -a ! -name %s\n' $(< excludelist)) \) -exec echo rm {} \;

Або , використовуючи -execз +термінатором замість цього.

find /path/to -type f \( ! -name "excludelist" $(printf ' -a ! -name %s\n' $(< excludelist)) \) -exec echo rm {} +

echo просто використовується для просушування.


-1

Моя пропозиція:

sed -e 's/^/\.\//' dont-delete.txt > dont-delete-relative-path.txt
find . -type f -print | grep -Fxvf dont-delete-relative-path.txt | xargs -d'\n' rm

Оновлення 2018-08-07

Приклад:

1: mkdir /tmp/delete-example && cd /tmp/delete-example
2: touch a b c d
3: echo "./a\n./b\n./dont-delete.txt\n" > dont-delete.txt
4: find . -type f -print | grep -Fxvf dont-delete.txt | xargs -d'\n' rm

Зауважте, що після рядка 3 у вас буде dont-delete.txtфайл із вмістом:

./a
./b
./dont-delete.txt

(Провідний ./це дуже важливо )

Файли cі dбудуть видалені.


Я спробував це з текстовим файлом імен файлів, розділених новим рядком. В кінцевому підсумку було видалено всі файли в каталозі.
Жак МАЛАПРЕЙД

Я думаю, що ваш "вести список" був неправильним.
nyxz

Я додав приклад використання.
nyxz
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.