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


    Феерическая расстановка точек над 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.
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 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
                                                              Однако у меня не ругается (в мане нет опции). Тоже 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 {} \;
                                                                                      

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