Удалить файлы, которых нет в списке шаблонов

Вы пробовали Evolution? http://projects.gnome.org/evolution/index.shtml

Я никогда не использовал его, но он должен поддерживать подключение к Exchange.

4
задан 22.03.2015, 14:53

7 ответов

Если Вы используете bash в качестве своей оболочки, то shopt -s extglob может активировать еще некоторые опции в шаблонах шарика. Например

!(5e1adcf7c9c1bcf8842c24f3bacbf169*|5e2497180424aa0d5a61c42162b03fef*)

будет соответствовать всем именам, не запускающимся с одной из двух строк.

1
ответ дан 05.10.2019, 12:02
  • 1
    Список очень длинен (500× 32char строки) и динамично вытянутый от базы данных. Это работало бы на маленькие исключения, но становится немного громоздким для моих целей. – Gray 22.03.2015, 18:44

При записи вопроса я начал играть вокруг с grep. Часть проблемы производительности - то, что grep выполняет тонну поисков regex каждого файла. Это дорого .

Мы можем просто сделать полные поиски строки без regex, с помощью -F аргумент.

find | grep -vFf <(
    sqlite3 database.sqlite3 'select replace(images, CHAR(124), CHAR(10)) from cars_car'
) ### | xargs rm

вывод является тем же и работает в 0,045 с.
старый занял 14,211 с.

<час>

Одной из проблем с парсингом ls являются проблематичные имена файлов. комментарий muru ниже выделяет довольно достойный способ использовать нулевые символы через весь конвейер.

find -print0 | grep -vzFf <(
    sqlite3 database.sqlite3 'select replace(images, CHAR(124), CHAR(10)) from cars_car'
) ### | xargs -0 rm

причина я не переключаюсь, мой основной ответ на это - то, что я знаю, что мои файлы всегда будут чистыми и что я выполнял это в wc -l, чтобы удостовериться, что я вижу корректное количество файлов для удаления.

3
ответ дан 05.10.2019, 12:02
  • 1
    Так... find . -iname '*.jpg' -print0 | grep -vzFf <(sqlite3 database.sqlite3 'select images from cars_car;' | sed 's/|/\n/g') | xargs -0 rm? Начиная с фиксированного сопоставления строк doesn' t автоматически подразумевают соответствие целого слова, таким образом foo отфильтрует и foo.jpg и foo_tn.jpg. – James C. 22.03.2015, 14:05
  • 2
    +1, но Вы могли бы хотеть упомянуть, что это перестало работать, если имена файлов содержат пробелы или новые строки. – shiva 22.03.2015, 14:07
  • 3
    @muru That' s хорошее изменение для обхождения проблематичных имен файлов но это делает проверку сколько файлов it' s выбирающий (с wc -l) немного более жесткий. – dtbarne 22.03.2015, 14:14
  • 4
    @Oli, в то время как we' ре в нем... grep -zc ^? – Mauvis Ledford 22.03.2015, 14:16

Вы могли просто удалить изображения в рамках выполнения сценария удаления продукта. Таким образом, загрузка будет балансироваться через каждое удаление продукта со временем. Дополнительно Вы не должны будете волноваться о выполнении сценария для чистки их вообще, и целое приложение будет автономной системой. Не говоря уже о нем решил бы проблему пространства с этой целью.

я понятия не имею, о котором DBMS Вы используете, ни о котором языке сценариев Вы используете для управления им или о том, как структура базы данных похожа (никакая идея о пути изображений также), но например, принимая MySQL как DBMS, PHP как язык сценариев и Products таблица в 1-many отношениях с Images таблица, с путем изображений, указывающим img папка, помещенная под корневым каталогом, это было бы что-то вроде этого:

<?php
    // ...
    $imgPath = $SERVER['DOCUMENT_ROOT'].'/img/';
    $result = mysqli_query($link, "SELECT Images.basename FROM Products, Images WHERE Products.productId = Images.productId AND Products.productId = $productId)
    while($row = mysqli_fetch_assoc($result)) {
        unlink($imgPath.$row['Images.basename'].'.jpg');
        unlink($imgPath.$row['Images.basename'].'_tn.jpg');
    }
    // ...
?>

, Если Вы заинтересованы [приблизительно 117] действия, Вы могли бы всегда использовать:

<?php
    // ...
    $imgPath = $SERVER['DOCUMENT_ROOT'].'/img/';
    $result = mysqli_query($link, "SELECT Images.basename FROM Products, Images WHERE Products.productId = Images.productId AND Products.productId = $productId)
    while($row = mysqli_fetch_assoc($result)) {
        shell_exec("rm {$imgPath}{$row['Images.basename']}*");
    }
    // ...
?>

Опасения по поводу них решение могло бы быть о дополнительном запросе, который необходимо будет выполнить каждый раз, если Вы уже не вытягиваете от Images прежде в сценарии и если это - беспокойство вообще.

1
ответ дан 05.10.2019, 12:02
  • 1
    Традиционно разговор, это - правильный ответ, но в моем случае, сценарий обновления удаляет все продукты и заменяет их. I' d должен реализовать выборочное удаление. Таким образом isn' t невозможный вообще, но it' s просто большее задание и I' m, не будучи оплаченным изящные решения. – Andrew Kotov 23.03.2015, 10:02

Я предположил бы, что это будет и более простым и быстрее, чтобы просто использовать GLOBIGNORE (предположение, что Ваша оболочка является ударом так или иначе):

   GLOBIGNORE
          A colon-separated list of patterns defining the set of filenames
          to be ignored by pathname expansion.  If a filename matched by a
          pathname expansion pattern also matches one of the  patterns  in
          GLOBIGNORE, it is removed from the list of matches.

Так, Вы могли просто считать шаблоны, которые Вы хотите из своего файла, добавляете * для создания их, шарики и преобразовать в двоеточие разделили список:

GLOBIGNORE=$(sqlite3 database.sqlite3 'select images from cars_car;' |
             sed 's/|/*:/g; s/$/*/')

Затем Вы можете всего rm все, и сбросить GLOBIGNORE (или просто закрыть текущий терминал):

rm * && GLOBIGNORE=""

, поскольку GLOBIGNORE будет теперь похож на это:

$ echo $GLOBIGNORE 
5e1adcf7c9c1bcf8842c24f3bacbf169*:5e2497180424aa0d5a61c42162b03fef*

Любые файлы, соответствующие тем шарикам, не будут включены в расширение *. Это обладает дополнительным преимуществом работы с любым типом имени файла, включая тех с пробелами, новыми строками или другими странными символами.

3
ответ дан 05.10.2019, 12:02
  • 1
    Кажется, что разделитель от sqlite |, таким образом sed 's/|/*:'? – phemt.latd 22.03.2015, 14:17
  • 2
    Это выглядит очень интересным. I' ll сравнивают его за минуту. – Giulio Piancastelli 22.03.2015, 14:19
  • 3
    @muru ах, да, спасибо. Я использовал файл с шаблоном на строку для тестирования. Фиксированный. – Community 22.03.2015, 14:24
  • 4
    @Oli, если Вы делаете, используют обновленный, который исправляет ошибку, которую нашел Muru. – Steffen Opel 22.03.2015, 14:25
  • 5
    Мой вывод SQL был немного более сложным, чем это. Это было разделено от канала и разделенный от строки, таким образом, каждая запись могла иметь тупики повторного изображения. I' ve обновил пример, таким образом, это просто откачивает разделенный от строки список тупиков с каналами, уже удаленными. – Gray 22.03.2015, 14:56

Долгосрочное решение, к которому я допускаю ошибку, является чем-то в конце моего сценария обновления (Python/Django). У меня есть список Автомобильных объектов — так больше базы данных querying—, который делает это еще быстрее. Это также происходит в точное время, старые изображения прекращают быть полезными.

я использую Python set, потому что это - вероятно, самый быстрый способ проверить. В это я добавляю все тупики изображений, которые я хочу сохранить, затем я выполняю итерации через миниатюры (легче к шарику) и удаляю файлы, которые не находятся в наборе.

# Generate a python "set" of image stubs
import itertools
imagehashes = set(itertools.chain(*map(lambda c: c.images.split('|'), cars)))

# Check which files aren't in the set and delete
import glob, os
for imhash in map(lambda i: i[25:-7], glob.glob('/path/to/images/*_tn.jpg')):
    if imhash in imagehashes:
        continue

    os.remove('/path/to/images/%s_tn.jpg' % imhash)
    os.remove('/path/to/images/%s.jpg' % imhash)

существует несколько приемов с map и itertools, чтобы сэкономить немного времени, но это главным образом сам объяснительное.

1
ответ дан 05.10.2019, 12:02

Когда чистый удар не сокращает его (или становится излишне неловким), пора переключиться на надлежащий язык сценариев. Мой предпочтительный инструмент обычно является Perl, но Вы могли использовать Python или Ruby или, heck, даже PHP для этого, если Вы предпочтете.

Однако вот простой сценарий Perl, который читает список префиксов от stdin (так как Вы не определили точно, как Вы получаете этот список), один на строку, и удаляет все файлы в текущем каталоге с .jpg суффикс, которые не имеют одного из этих префиксов:

#!/usr/bin/perl
use strict;
use warnings;

my @prefixes = <>;
chomp @prefixes;
# if you need to do any further input mangling, do it here

my $regex = join "|", map quotemeta, @prefixes;
$regex = qr/^($regex)/;   # anchor the regex and precompile it

foreach my $filename (<*.jpg>) {
    next if $filename =~ $regex;
    unlink $filename or warn "Error deleting $filename: $!\n";
}

, Если Вы предпочли бы, можно сжать это вниз до остроты, например:

perl -e '$re = "^(" . join("|", map { chomp; "\Q

Когда чистый удар не сокращает его (или становится излишне неловким), пора переключиться на надлежащий язык сценариев. Мой предпочтительный инструмент обычно является Perl, но Вы могли использовать Python или Ruby или, heck, даже PHP для этого, если Вы предпочтете.

Однако вот простой сценарий Perl, который читает список префиксов от stdin (так как Вы не определили точно, как Вы получаете этот список), один на строку, и удаляет все файлы в текущем каталоге с .jpg суффикс, которые не имеют одного из этих префиксов:

[110]

, Если Вы предпочли бы, можно сжать это вниз до остроты, например:

[111] <час>

ps В Вашем случае, так как достаточно легко извлечь префикс из имен файлов, Вы могли также использовать хеш вместо regex для оптимизации поиска, как это:

my %hash;
undef @hash{@prefixes};   # fastest way to add keys to a hash

foreach my $filename (<*.jpg>) {
    my ($prefix) = ($filename =~ /^([0-9a-f]+)/);
    next if exists $hash{$prefix};
    unlink $filename or warn "Error deleting $filename: $!\n";
}

Однако даже при том, что этот метод масштабируется лучше асимптотически (по крайней мере, на практике; в теории regex механизм мог оптимизировать соответствие для масштабирования, а также метод хеша), всего для 500 префиксов вообще нет никакого заметного различия.

, По крайней мере, на текущих версиях Perl, однако, regex решение становится намного медленнее, как только количество альтернатив превышает определенный предел. Для 32-байтовых префиксов, мое тестирование показало крупный переход во время выполнения, когда количество альтернатив достигло 6553, но точный порог, по-видимому, также зависит от длины префиксов и на том, что еще, во всяком случае, содержит regex. Это - по-видимому, причуда Perl regex механизм и его оптимизатор, таким образом, другие regex реализации (даже PCRE) могут показать различное поведение.

" } <>) . ")"; unlink grep !/$re/, <*.jpg>'
<час>

ps В Вашем случае, так как достаточно легко извлечь префикс из имен файлов, Вы могли также использовать хеш вместо regex для оптимизации поиска, как это:

my %hash;
undef @hash{@prefixes};   # fastest way to add keys to a hash

foreach my $filename (<*.jpg>) {
    my ($prefix) = ($filename =~ /^([0-9a-f]+)/);
    next if exists $hash{$prefix};
    unlink $filename or warn "Error deleting $filename: $!\n";
}

Однако даже при том, что этот метод масштабируется лучше асимптотически (по крайней мере, на практике; в теории regex механизм мог оптимизировать соответствие для масштабирования, а также метод хеша), всего для 500 префиксов вообще нет никакого заметного различия.

, По крайней мере, на текущих версиях Perl, однако, regex решение становится намного медленнее, как только количество альтернатив превышает определенный предел. Для 32-байтовых префиксов, мое тестирование показало крупный переход во время выполнения, когда количество альтернатив достигло 6553, но точный порог, по-видимому, также зависит от длины префиксов и на том, что еще, во всяком случае, содержит regex. Это - по-видимому, причуда Perl regex механизм и его оптимизатор, таким образом, другие regex реализации (даже PCRE) могут показать различное поведение.

1
ответ дан 05.10.2019, 12:02

запрос быстр, таким образом, это эти grep бит, который срывает все это.

Другое решение состояло бы в том, чтобы просто инвертировать запрос, так, чтобы можно было передать результаты по каналу к rm непосредственно.

Это не должно представлять различие в синхронизации вообще.

0
ответ дан 05.10.2019, 12:02
  • 1
    Проблема с тем (на мой комментарий к Вашему другому ответу) состоит в том что этой точкой база данных doesn' t имеют имена файлов файлов, которые мы хотим удалить, только те мы хотим сохранить. Следовательно будучи должен пересечь его со списком файлов, которые существуют. – Phil 23.03.2015, 12:52
  • 2
    @Oli На самом деле на Ваш комментарий, я принял Вас, мог: " в моем случае сценарий обновления удаляет все продукты и заменяет them". Вы возражаете, объясняет эта строка? I' m, вероятно, неправильно понимая его. – BozoJoe 23.03.2015, 13:00
  • 3
    Уверенный. Три раза в день мы выводим базу данных и полностью повторно создаем ее от внешнего источника данных. Нет никакого промежуточного шага, где я проверяю, чтобы видеть, был ли продукт удален между двумя версиями базы данных (и следовательно, запросите те объекты для их файлов). Записей уже не стало. Как я сказал прежде, я мог изменение, как это работает, но I' d скорее не начинают изменяться, как зрелая и критическая производственная система работает, если я могу помочь ей. – Joshua Smith 23.03.2015, 13:04
  • 4
    @Oli хорошо я видел обновленный комментарий. Тогда я can' t воображают другой способ заставить инвертированный список файлов оставаться кроме путем выполнения пересечения так или иначе, которое наличие квадратичной стоимости является дорогой вещью здесь. Я вижу проблему. – Răzvan Flavius Panda 23.03.2015, 13:45

Теги

Похожие вопросы