удалить файл, но исключить все файлы в списке


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

Я надеюсь, что вы не его учитель. лол
Гуджарат Сантана

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попросить подтверждение перед выполнением.

спасибо за вашу помощь, теперь у меня есть решение!
stefan83

@gardenhead, я устал от вашего кода, но он удаляет все файлы в каталоге и сохраняет только первый и последний файл в списке dont-delete. у вас есть идеи для этой проблемы? заранее спасибо.
Негар

1

Похоже, что вы можете сделать это изначально zsh, используя (+cmd)квалификатор glob.

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

 % 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значение on, вот несколько более консервативная альтернатива:

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

Explaination:

  • 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команду выше, чтобы удалить файлы, которых нет в файле excludelist . Если находка не имеет -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.