Пользователь
0,0
рейтинг
7 ноября 2012 в 13:50

Администрирование → Так как же удалить миллионы файлов из одной папки?


Феерическая расстановка точек над i в вопросе удаления файлов из переполненной директории.

Прочитал статью Необычное переполнение жесткого диска или как удалить миллионы файлов из одной папки и очень удивился. Неужели в стандартном инструментарии Linux нет простых средств для работы с переполненными директориями и необходимо прибегать к столь низкоуровневым способам, как вызов getdents() напрямую.

Для тех, кто не в курсе проблемы, краткое описание: если вы случайно создали в одной директории огромное количество файлов без иерархии — т.е. от 5 млн файлов, лежащих в одной единственной плоской директории, то быстро удалить их не получится. Кроме того, не все утилиты в linux могут это сделать в принципе — либо будут сильно нагружать процессор/HDD, либо займут очень много памяти.

Так что я выделил время, организовал тестовый полигон и попробовал различные средства, как предложенные в комментариях, так и найденные в различных статьях и свои собственные.

Подготовка


Так как создавать переполненную директорию на своём HDD рабочего компьютера, потом мучиться с её удалением ну никак не хочется, создадим виртуальную ФС в отдельном файле и примонтируем её через loop-устройство. К счастью, в Linux с этим всё просто.

Создаём пустой файл размером 200Гб
#!python
f = open("sparse", "w")
f.seek(1024 * 1024 * 1024 * 200)
f.write("\0")

Многие советуют использовать для этого утилиту dd, например dd if=/dev/zero of=disk-image bs=1M count=1M, но это работает несравнимо медленнее, а результат, как я понимаю, одинаковый.

Форматируем файл в ext4 и монтируем его как файловую систему
mkfs -t ext4 -q sparse  # TODO: less FS size, but change -N option
sudo mount sparse /mnt
mkdir /mnt/test_dir

К сожалению, я узнал об опции -N команды mkfs.ext4 уже после экспериментов. Она позволяет увеличить лимит на количество inode на FS, не увеличивая размер файла образа. Но, с другой стороны, стандартные настройки — ближе к реальным условиям.

Создаем множество пустых файлов (будет работать несколько часов)
#!python
for i in xrange(0, 13107300):
    f = open("/mnt/test_dir/{0}_{0}_{0}_{0}".format(i), "w")
    f.close()
    if i % 10000 == 0:
        print i

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

Проверяем, что все айноды на ФС исчерпаны.
$ df -i
/dev/loop0      13107200      13107200     38517           100% /mnt

Размер файла директории ~360Мб
$ ls -lh /mnt/
drwxrwxr-x 2 seriy seriy 358M нояб.  1 03:11 test_dir

Теперь попробуем удалить эту директорию со всем её содержимым различными способами.

Тесты


После каждого теста сбрасываем кеш файловой системы
sudo sh -c 'sync && echo 1 > /proc/sys/vm/drop_caches'
для того чтобы не занять быстро всю память и сравнивать скорость удаления в одинаковых условиях.

Удаление через rm -r


$ rm -r /mnt/test_dir/
Под strace несколько раз подряд (!!!) вызывает getdents(), затем очень много вызывает unlinkat() и так в цикле. Занял 30Мб RAM, не растет.
Удаляет содержимое успешно.
iotop
 7664 be/4 seriy      72.70 M/s    0.00 B/s  0.00 % 93.15 % rm -r /mnt/test_dir/
 5919 be/0 root       80.77 M/s   16.48 M/s  0.00 % 80.68 % [loop0]

Т.е. удалять переполненные директории с помощью rm -r /путь/до/директории вполне нормально.

Удаление через rm ./*


$ rm /mnt/test_dir/*
Запускает дочерний процесс шелла, который дорос до 600Мб, прибил по ^C. Ничего не удалил.
Очевидно, что glob по звёздочке обрабатывается самим шеллом, накапливается в памяти и передается команде rm после того как считается директория целиком.

Удаление через find -exec


$ find /mnt/test_dir/ -type f -exec rm -v {} \;
Под strace вызывает только getdents(). процесс find вырос до 600Мб, прибил по ^C. Ничего не удалил.
find действует так же, как и * в шелле — сперва строит полный список в памяти.

Удаление через find -delete


$ find /mnt/test_dir/ -type f -delete
Вырос до 600Мб, прибил по ^C. Ничего не удалил.
Аналогично предыдущей команде. И это крайне удивительно! На эту команду я возлагал надежду изначально.

Удаление через ls -f и xargs


$ cd /mnt/test_dir/ ; ls -f . | xargs -n 100 rm
параметр -f говорит, что не нужно сортировать список файлов.
Создает такую иерархию процессов:
 | - ls 212Кб
 | - xargs 108Кб
    | - rm 130Кб # pid у rm постоянно меняется

Удаляет успешно.
iotop  # сильно скачет
 5919 be/0 root        5.87 M/s    6.28 M/s  0.00 % 89.15 % [loop0]

ls -f в данной ситуации ведет себя адекватнее, чем find и не накапливает список файлов в памяти без необходимости. ls без параметров (как и find) — считывает список файлов в память целиком. Очевидно, для сортировки. Но этот способ плох тем, что постоянно вызывает rm, чем создается дополнительный оверхед.
Из этого вытекает ещё один способ — можно вывод ls -f перенаправить в файл и затем удалить содержимое директории по этому списку.

Удаление через perl readdir


$ perl -e 'chdir "/mnt/test_dir/" or die; opendir D, "."; while ($n = readdir D) { unlink $n }' (взял здесь)
Под strace один раз вызывает getdents(), потом много раз unlink() и так в цикле. Занял 380Кб памяти, не растет.
Удаляет успешно.
iotop
 7591 be/4 seriy      13.74 M/s    0.00 B/s  0.00 % 98.95 % perl -e chdi...
 5919 be/0 root       11.18 M/s 1438.88 K/s  0.00 % 93.85 % [loop0]

Получается, что использование readdir вполне возможно?

Удаление через программу на C readdir + unlink


//file: cleandir.c
#include <dirent.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
    struct dirent *entry;
    DIR *dp;
    chdir("/mnt/test_dir");
    dp = opendir(".");
    while( (entry = readdir(dp)) != NULL ) {
        if ( strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..") ){
            unlink(entry->d_name);  // maybe unlinkat ?
        }
    }
}

$ gcc -o cleandir cleandir.c
$ ./cleandir
Под strace один раз вызывает getdents(), потом много раз unlink() и так в цикле. Занял 128Кб памяти, не растет.
Удаляет успешно.
iotop:
 7565 be/4 seriy      11.70 M/s    0.00 B/s  0.00 % 98.88 % ./cleandir
 5919 be/0 root       12.97 M/s 1079.23 K/s  0.00 % 92.42 % [loop0]

Опять — же, убеждаемся, что использовать readdir — вполне нормально, если не накапливать результаты в памяти, а удалять файлы сразу.

Выводы


  • Использовать комбинацию функций readdir() + unlink() для удаления директорий, содержащих миллионы файлов, можно.
  • На практике лучше использовать rm -r /my/dir/, т.к. он поступает более умно — сперва строит относительно небольшой список файлов в памяти, вызывая несколько раз readdir(), а затем удаляет файлы по этому списку. Это позволяет более плавно чередовать нагрузку на чтение и запись, чем повышает скорость удаления.
  • Для снижения нагрузки на систему использовать в комбинации с nice или ionice. Либо использовать скриптовые языки и вставлять небольшие sleep() в циклах. Либо генерировать список файлов через ls -l и пропускать его через замедляющий пайп.
  • Не верить всему, что пишут в интернетах, конечно же! В различных блогах часто обсуждают эту проблему, и регулярно подсказывают неработающие решения.

P.S.: К сожалению, не нашел в Python функции для итеративного чтения директории, чему крайне удивлён; os.listdir() и os.walk() читают директорию целиком. Даже в PHP есть readdir.
Сергей @seriyPS
карма
121,7
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Администрирование

Комментарии (101)

  • +13
    мгновенное создание 200G
    dd if=/dev/zero of=./disk-image bs=1M count=0 seek=200G
    • +18
      Только bs дожен быть просто 1, без суффикса M, иначе это создание файла размером в 200000 ПБ
      dd if=/dev/zero of=./disk-image bs=1 count=0 seek=200G
      • +1
        Точно, так работает.

        dlinyj@dlinyj-System:~/tmp/test$ dd if=/dev/zero of=./disk-image bs=1 count=0 seek=200M
        0+0 записей считано
        0+0 записей написано
        скопировано 0 байт (0 B), 2,2231e-05 c, 0,0 kB/c
        dlinyj@dlinyj-System:~/tmp/test$ ls -la
        итого 8
        drwxrwxr-x 2 dlinyj dlinyj 4096 нояб. 7 14:24.
        drwxrwxr-x 15 dlinyj dlinyj 4096 нояб. 7 14:24…
        -rw-rw-r-- 1 dlinyj dlinyj 209715200 нояб. 7 14:26 disk-image
        dlinyj@dlinyj-System:~/tmp/test$

      • 0
        Да, это работает. Спасибо!
      • 0
        Для создания swap файла такой метод не прокатит.
        • +3
          fallocate -l 1G swapfile

          так прокатит (если у вас btrfs, ext4, ocfs2, или xfs)
        • 0
          Почему?
          • 0
            Swap files cannot be sparse; they must be fully allocated before using them.
    • 0
      Ubuntu 12.04, в связи с недостатком места для размещения 200 гигабайт, решил обойтись 200 мегабайтами.

      dlinyj@dlinyj-System:~/tmp/test$ dd if=/dev/zero of=./disk-image bs=1M count=0 seek=200M
      dd: не удалось выполнить отсечение на 219902325555200 байт в выходном файле «./disk-image»: Файл слишком велик
      dlinyj@dlinyj-System:~/tmp/test$ ls -la
      итого 8
      drwxrwxr-x 2 dlinyj dlinyj 4096 нояб. 7 14:24.
      drwxrwxr-x 15 dlinyj dlinyj 4096 нояб. 7 14:24…
      -rw-rw-r-- 1 dlinyj dlinyj 0 нояб. 7 14:24 disk-image
      dlinyj@dlinyj-System:~/tmp/test$

      • +2
        bs=1M
        Выше написали же. У вас файл 2000G получается.
        • +1
          Да, я уже понял. Спасибо!
          • +3
            а я теперь всегда буду… ну вы поняли :)
    • 0
      Я, кстати, понял чем отличается файл, созданный с помощью seek от файла честно заполненного нулями…
      $ ls -lh disk*
      -rw-rw-r-- 1 seriy seriy 600G нояб.  7 14:29 disk-image
      -rw-rw-r-- 1 seriy seriy 600G нояб.  7 14:33 disk-image2
      -rw-rw-r-- 1 seriy seriy 600G нояб.  7 14:33 disk-image3
      -rw-rw-r-- 1 seriy seriy 600G нояб.  7 14:33 disk-image4
      $ df -h | grep /home
      /dev/sda5        1,8T         822G  900G           48% /home

      Т.е. файл хоть и весит 600Гб, но места на диске не занимает. Возможно это какая-то оптимизация файловой системы, не знаю.
      • +5
        Это обычный sparse-файл
      • +3
        Да, это sparse file что, кстати, и отражено в его названии в статье ;-)

        P. S. Почему коментарий можно редактировать, но нельзя удалить? Дубликат же получился :-(
        • 0
          А python'овские seek и write, как понимаю, тоже создадут такой файл? Иначе он никак не мог бы отработать быстрее.
          • +4
            Они не столько python'овские, сколько syscall'ы, у Питона просто обертка. Так что да.
    • +3
      truncate -s 200G disk-image
  • +1
    Использую

    find. | xargs rm

    Пока не подводило.
    • +2
      Попробовал — отжирает память и ничего не удаляет.
      Не исключаю, что от версии зависит.
      find (GNU findutils) 4.4.2
      • 0
        find -type f -print0 | xargs -0 /bin/rm -f

        А так?
        • 0
          А разница будет?
          Когда запускается xargs? Когда find выдаст весь результат или по мере появления?
          • 0
            Сразу, но есть тонкости. Скорее всего, будет чуть быстрее.

            Попробуйте ещё:

            find. -type f -print0 | xargs -r0 ionice rm -f
  • –3
    А сделайте тоже самое на reiserfs 3
    • –3
      Или на xfs. Автору нужно правильно выбирать файловые системы под задачу.
      • +10
        Дык сами и попробуйте. Какая разница с какой файловой системы rm -r ./* загрузит 20 миллионов список файлов и уйдёт в своп?
        Статья не о выборе правильной ФС, а, скорее, об инструментах для работы с ней.
  • +2
    find /mnt/test_dir/ -type f -delete
    
    — удаляю периодически этим порядка миллиона файлов, да еще и через сетевую ФС. На продакшне под нагрузками, проблем не замечено. Тоже выбирал как то чем удалять, вот пришел.
    • +3
      Между миллионом и 13 млн файлов существенная разница! Вы не смотрели сколько RAM занимает find при этом?

      Мой опыт показывает, что find всегда строит в памяти полный список файлов в директории прежде чем начать проводить с ними какие-то операции.

      Может расскажете как вы пришли к такому решению
      • +1
        Что разница есть, ну есть, трудно спорить) Пришел к этому тестами. Память не помню, но прожорливой рядом джаве оно не мешало. Обычно вывод отправляю в лог файлик и там видно, что старт начинается через пару минут, а не после сбора полного списка, плюс проходит оно пачками.
        • +1
          Попробовал. Отъело 3.5Гб и только после этого начало удалять. Не знаю весь список оно загрузило или нет, но то что это не лучше rm -r по моему очевидно.
          • 0
            Верю как вам так и себе:) У меня ситуация стабильная. К сожалению нет времени сделать пару тестов на моем хранилище с этим.
      • +1
        find не всегда строит, а смотрит на количество оставшейся памяти и пытается балансировать между минимизацией вызовов readdir и потреблением памяти. Попробуйте запустить его под ulimit — хорошо иллюстрирует.
        • +1
          Подскажите какие ulimit задавать?

          попробовал ulimit -m $((1024 * 50)) — не помогло. Так же набирает по 500 Мб
          с ulimit -v $((1024 * 50)) после 50Мб молча падает
          • +3
            Если речь о GNU'шном find, то он при своей работе заводит списки структур savedir_dirinfo, которые пытается инкрементально порождать на каждую директорию, начиная выделение с xmalloc на 16 байт, а затем растягивая получившуюся систему буферов с помощью extendbuf, которая в свою очередь выбирает размер нового буфера тупым нахождением округления сверху (в общем, практически чуть ли не первая реализация из классической книжки Кнута):

              while (newsize < wanted)
                {
                  if (2 * newsize < newsize)
                    xalloc_die ();
                  newsize *= 2;
                }
              return newsize;
            

            Дальше, если новый объем пытается выделиться или реаллоцироваться через xmalloc / xrealloc и если не получится это сделать, то find отнюдь не впадает в панику и не считает это критической ошибкой, а, напротив, считает, что все, памяти уже достаточно и возвращается к предыдущему объему — и вот тут-то и должен сработать механизм дампа всего накопившегося, если это возможно (разумеется, нужно отсутствие флага SavedirSort, иначе будет вызван qsort и будет делаться сортировка вывода, что в данном случае смерти подобно).

            С точки зрения использования ulimit: -m в современных Linux вообще смысла не имеет, см. RLIMIT_RSS, можно попробовать -d (но тогда и смотреть надо на data segment и учесть, что там бывает только софт-лимит), ну и по идее должен работать -v — там просто проблема в том, что он еще и стек лимитирует и при исчерпании стека будет предсказуемо SIGSEGV — возможно, вы столкнулись именно с этой ситуацией. Можно попробовать лимитировать -v, но сделать -s побольше. Ну или вообще не трогать ulimit, а лимитировать через cgroups.
            • 0
              Попробовал ограничить через cgroups
              sudo cgcreate -a $USER -g memory:tst
              echo 700000000 > /sys/fs/cgroup/memory/tst/memory.limit_in_bytes
              sudo cgexec -g memory:tst find /mnt/ext4/test_dir/ -delete
              

              При отключенном swap процесс прибивается по достижении 700Мб, если swap включен — уходит в swap.
              Но так ничего и не удаляет.
              Если не поленюсь — запущу под виртуальной машиной.
              • 0
                Можете уточнить, что значит «прибивается»? Это все-таки SIGSEGV или oomkiller, скажем, у вас как-то агрессивно срабатывает?
                • 0
                  Судя по syslog это oom killer

                  Nov 10 22:08:39 seriy-desktop kernel: [ 1772.927181] find invoked oom-killer: gfp_mask=0xd0, order=0, oom_adj=0, oom_score_adj=0
                  Nov 10 22:08:39 seriy-desktop kernel: [ 1772.927186] find cpuset=/ mems_allowed=0
                  ..........
                  Nov 10 22:08:39 seriy-desktop kernel: [ 1772.927269] Task in /tst killed as a result of limit of /tst
                  Nov 10 22:08:39 seriy-desktop kernel: [ 1772.927272] memory: usage 292972kB, limit 292972kB, failcnt 55186
                  Nov 10 22:08:39 seriy-desktop kernel: [ 1772.927274] memory+swap: usage 292972kB, limit 9007199254740991kB, failcnt 0
                  ...........
                  Nov 10 22:08:39 seriy-desktop kernel: [ 1772.949829] [ pid ]   uid  tgid total_vm      rss cpu oom_adj oom_score_adj name
                  Nov 10 22:08:39 seriy-desktop kernel: [ 1772.949893] [ 5616]     0  5616    77136    73379   2       0             0 find
                  Nov 10 22:08:39 seriy-desktop kernel: [ 1772.949895] Memory cgroup out of memory: Kill process 5616 (find) score 974 or sacrifice child
                  Nov 10 22:08:39 seriy-desktop kernel: [ 1772.949898] Killed process 5616 (find) total-vm:308544kB, anon-rss:292736kB, file-rss:780kB

                  • 0
                    Ну, тогда как бы эксперимента не получается: oom killer убивает find еще до того, как он успеет узнать, что памяти ему не дали и поумерить свои аппетиты. Попробуйте выключить oom_killer для этой cgroup, что-то типа:

                    echo 1 >/sys/fs/cgroup/memory/tst/oom_control
                    
                    • 0
                      echo 1 >/sys/fs/cgroup/memory/tst/memory.oom_control
                      
                      Теперь дорастает до 300Мб и засыпает на __mem_cgroup_try_charge.
                      Ладно, по-моему отклонились от темы.
                      Нет причин вам не верить. Но, похоже, find не успокаивается пока не займёт всю доступную память, включая swap. Что есть не самое удачное поведение.
                      • 0
                        Ну, мне на самом деле самому интересно, что на современных системах получается. Насколько я понимаю, поведения вида «займем всю выданную память» с некоей авторегуляцией процесса занятия со стороны программы, на современных системах (и с точки зрения аппаратного развития — всякие там SMP, multi core и т.д., и с точки зрения программного — OOM killers, cgroups, поломанные setrlimit), мягко говоря, работает плохо.

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

                        А с C-based все получается именно что вот так грустно: либо непортабельно, либо не особенно работоспособно, либо и то, и другое…
      • –1
        А если так?
        find /mnt/test_dir/ -delete
        • 0
          В статье есть такой пример. Опция -type f не влияет.
    • +3
      find -type f не всегда помогает, иногда приходится делать find -not -type d
  • 0
    >>>К сожалению, не нашел в Python функции для итеративного чтения директории, чему крайне удивлён; os.listdir() и os.walk() читают директорию целиком
    Судя по документации, os.listdir() должна возвращать список сразу, в то время как os.walk() как раз отдаёт результат постепенно.
    os.listdir(path) — Return a list containing the names of the entries in the directory given by path.
    os.walk(top, topdown=True, onerror=None, followlinks=False) — Generate the file names in a directory tree by walking the tree either top-down or bottom-up. For each directory in the tree rooted at directory top (including top itself), it yields a 3-tuple (dirpath, dirnames, filenames).
    • 0
      Вот прям сейчас специально проверил
      import os
      
      for l in os.walk("/mnt/test_dir/"):
          print l
      

      Жрет память, ничего не печатает
      • +1
        А glob.iglob не помогает?
        • +1
          Сам себе отвечу. Судя по всему glob использует тот же listdir. Так что не поможет.
    • +6
      Вы не выделили жирным.
      it yields a 3-tuple (dirpath, dirnames, filenames).
      filenames is a list of the names of the non-directory files in dirpath.
  • –36
    В Linux никак нельзя удалить файлы из папки, в Linux нет папок, как и мамок, как и дедок.
    В Linux есть директории.
    • +33
      Нет никаких директорий, вам наврали, есть иноды с различными аттрибутами )
    • +5
      … и есть там только железяка, с куском рандомно намагниченных точек, вращающаяся в пространстве вокруг своей оси. И смотрите вы сейчас не на сайт, и даже не на браузер, а на тонкий слой жидких кристаллов, меняющих свою поляризацию в зависимости от электрических импульсов…
      • +4
        Да тоже не факт в случае SSD :)
        • +5
          И CRT-монитора :)
    • –15
      я гляжу виндузятник позвал дружков неудачников за указание ошибки.
      неплохо-неплохо.
      вот только от туповатых виндузятников директория папкой не станет.
      • +3
        Заголовок статьи я взял от первоначальной статьи habrahabr.ru/post/152193/
        В тексте статьи везде использую «директория».
        • –16
          Да хоть откуда берите. Сразу палиться виндузятник. Папка — пьяный бред переводчиков какой-то из их недоос.
          • +2
            о, а не подскажете, как трезвый линуксоид в здравом уме перевел бы слово «folder»?
          • +4
            Неграмотный фанатик тоже сразу «палитЬся».

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

            А ещё в Фигурнове было слово «каталог».
    • +5
      «директории», ежливчо, это ещё более пьяный бред причом даже не переводчика.

      Правильное, русское, слово — каталоги.
      Но всем пофик.
      • 0
        > «директории», ежливчо, это ещё более пьяный бред причом даже не переводчика.

        Ну почему, Уфимская директория или Французская директория — очень даже не бред :)
  • +1
    Спасибо хорошая статья ))
  • –2
    Да прекратите уже размусоливать…

    rm -rfd /path
    


    100 раз уже и на опеннете писалось, и в предыдущем топике я об этом упоминал.
    • 0
      Я пробовал. У меня ругнулся что нет опции "-d".
      $ rm --version rm (GNU coreutils) 8.13

      Кстати, судя по ману (man 2 unlink), unlink не удаляет директории

      ERRORS
      ....
             EISDIR pathname refers to a directory.  (This is  the  non-POSIX  value
                    returned by Linux since 2.1.132.)

      • –2
        Не знаю, у меня вот rm (GNU coreutils) 7.4
        И хоть ключика нет в --help, он есть и работает ровно так, как я того ожидаю.
        Прочитал про него давным давно на LOR.
        • +1
          А исходники читать не пробовали? Нет там такой опции
          • –2
            Когда ее там нет, я вижу ошибку
            rm: invalid option -- 'd'
            
            • +5
              while ((c = getopt_long (argc, argv, "dfirvIR", long_opts, NULL)) != -1)
                  {
                    switch (c)
                      {
                      case 'd':
                        /* Ignore this option, for backward compatibility with
                           coreutils 5.92.  FIXME: Some time after 2005, change this
                           to report an error (or perhaps behave like FreeBSD does)
                           instead of ignoring the option.  */
                        break;
              
              
              • +4
                Собственно, опция-то есть! Нет реализации :)
              • 0
                Ну, я последний раз пользовался ею в дебиане 4-м, сейчас вся мелочь в memcache живет.
                Тогда оно реально сработало и сильно облегчило мне жизнь.
              • +4
                >Some time after 2005, change this…

                Ну, надо подождать до 2005-го, чего не ясно?
                • +2
                  >… to report an error
                  Это исходник версии 7.4 (2006 год). В текущих версиях этого фрагмента нет, и rm ругается на опцию -d.
                  Вы что сказать то хотели?
                  • +1
                    А в мане от 8.5 (Debian 6.0.6) кстати есть.
                    -d, --directory
                    Удалять каталоги с помощью системного вызова unlink(2) вместо rmdir(2), и не требовать, чтобы каталог был пуст перед его разлинковкой. Работает только если у вас есть соответствующие привилегии. Поскольку разлинковка (разрыв связи) каталога приводит к тому, что все файлы в удаленном каталоге теряют ссылочную целостность, то будет благоразумно после этой операции проверить файловую систему командой fsck(8).

                    Но заметьте, советуют проверить диск :)
                    • 0
                      Это просто старый перевод, в английской (LANG=c man rm) этой опции нет, равно как и в выводе rm --help (для обоих языков) и, конечно же, в info-странице
                      • 0
                        edited
          • 0
            Однако у меня не ругается (в мане нет опции). Тоже GNU coreutils 7.4
            Вот можно почитать: www.opennet.ru/man.shtml?topic=rm&category=1&russian=0
          • –2
            Да, залез в исходники, нет кода :( Заколдовали… БСД рулит, там есть :)
        • +1
          Ключ -d есть во FreeBSD

          $ man rm
          RM(1)                   FreeBSD General Commands Manual                  RM(1)
          
          NAME
               rm, unlink -- remove directory entries
          
          SYNOPSIS
               rm [-f | -i] [-dIPRrvW] file ...
               unlink file
          
          DESCRIPTION
               The rm utility attempts to remove the non-directory type files specified
               on the command line.  If the permissions of the file do not permit writ-
               ing, and the standard input device is a terminal, the user is prompted
               (on the standard error output) for confirmation.
          
               The options are as follows:
          
               -d      Attempt to remove directories as well as other types of files.
          
    • +1
      $ rm -rfd 4444/
      rm: invalid option — 'd'
      Try `rm --help' for more information.

      $ cat /etc/lsb-release |grep DISTRIB_DESCRIPTION
      DISTRIB_DESCRIPTION=«Ubuntu 12.04.1 LTS»

      $ uname -a
      Linux relgames 3.2.0-32-generic #51-Ubuntu SMP Wed Sep 26 21:33:09 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux
      • –1
        Зачем выкладывать версию ядра в обсуждении особенностей парсинга программой коммандой строки?
    • 0
      Кстати, интересно вот что… После того как мы сделали rm -d у нас все айноды освободятся?
      А fsck после этого не будет ли работать дольше чем rm -rf?
      А после fsck не появятся ли все эти файлы в lost+found?
      В общем, нужно проверять.
      • +3
        Тут проблема не в дольше или нет, а в том, что придется, возможно, размонтировать ФС :)
  • 0
    А вот интересно, в unix-е каталог, это тоже файл. Нужно попытаться этот файл каким-то образом обнулить.
    >dir — не прокатит, конечно :) Но может через системный вызов какой?
    Хотя, даже если это и удастся сделать, после этого, наверняка, придется использовать fsck и чистить lost+found.
    • 0
      Именно так.
      Причом в lost+found образуется тежесамые миллионы файлов.
  • +2
    На какой файловой системе тестировали?

    У меня была проблема с ext3.

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

    Способы с rm тоже работали (удаляли файлы) но на время работы делали сервер неработоспособным из-за того, что удаление забивало полностью диск и другие процессы не могли нормально с него читать
    • 0
      В статье, если приглядеться, указано что ext4. Не думаю, что это сильно повлияло, но могу попробовать с ext3 тоже.
      Для снижения нагрузки см 3-й пункт в выводах статьи.
      • 0
        ext3 и ext4 в плане удаления файлов сильно отличаются (по крайней мере по скорости выполнения точно)
        • 0
          Ок, попробую ext3

          Но, опять таки, главная проблема не в скорости удаления/чтения, а в том накапливается список файлов в памяти или используется сразу.
          • 0
            Ещё необходимо мониторить free, т.к. память может отожрать само ядро.
  • +1
    Года три назад была ситуация, когда файлы сессий создавались, но не удалялись :D, в итоге на диске в 1ТБ было занято около 75% инодов. Сколько там было файлов не известно. Удаление стандартными средствами не давало результатов, либо было слишком медленным.

    Ситуацию спас find, с поиском по инодам.

    find . -inum 0000000 -exec rm -i {} \
    
  • +2
    Для того чтоб айнодов было больше можно уменьшить размер блока в ext4:

    mkfs -t ext4 -b 2048
    

    В этом случае минимальный размер папки будет не 4Kb а 2Kb а айнодов будет в 2 раза больше

    или
    mkfs -t ext4 -b 1024 
    

    В этом случае минимальный размер папки будет 1Kb а айнодов будет в 4 раза больше
    • 0
      В статье упоминается опция -I, которая позволяет непосредственно задать нужное количество айнодов. Но я о ней узнал не сразу, поэтому использовал опции по умолчанию.
    • 0
      Спасибо за подсказку в личке, правильный ключ -N, а не -I.
  • +2
    Было такое очень давно (тоже с какими-то сессиями). Кажется помогло что-то типа
    rm 00* потом rm 01* потом… rm ff*
    ничего лучше на тот момент не придумал.
  • +2
    Для большого кол-ва элементов в exec'е find'а стоит использовать + вместо;

    find /mnt/test_dir/ -type f -exec rm -v {} \+

    Если верить ману, ведет себя схоже с xargs.
    • 0
      Работает в таком виде, если не надо удалять все файлы:
      find /oth/monitor/ -name "*2013*.wav" -type f -exec rm -v {} \+
  • 0
    А как же Argument list too long в rm?
    Возможно ли, что поведение rm зависит от ОС/ядра?
    • +1
      В случае rm -r /mnt/test_dir/ аргумент всего один — /mnt/test_dir/. Вот если использовать find или
      rm -r /mnt/test_dir/* то да, получишь «Argument list too long».
  • –4
    Я в свое время использовал простейщий однострочник
    for i in /mnt/test_dir/*; do rm -f $i; done
    Конечно, не самый оптимальный вариант, т.к. удаляет по одному файлу, но проблема стояла остро и решить ее надо было прямо сейчас.
    ФС была ext3, файлов не 5 миллионов, конечно, но не меньше 1 миллиона.
  • 0
    А когда отменили ARG_MAX?
    смысл xargs не запускаться по 100 аргументов (-n100), а запускаться с столькими аргументами, сколько влезет в ARG_MAX.
    вам можно было бы миллион, например, поставить, если нет ARG_MAX
  • 0
    find /var/lib/php/session -depth -mindepth 1 -maxdepth 1 -type f -ctime +ХХХ -execdir fuser -s {} 2>/dev/null \; -delete
    До сих пор не могу понять этот шаман-код Плеска.
  • 0
    Заменил в коде
    chdir("/mnt/test_dir"); на chdir(argv[1]);
    


    Вызываю так, что позволяет рекурсивно обходить каталоги и зачищать их
    find /backup/daily.5/ -type d -printf "Cleaning: %p\n" -exec /usr/bin/cleandir {} \;
    

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.