Эффективно удалить большой каталог, содержащий тысячи файлов

162

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

Есть так много файлов, что выполнение rm -rfвозвращает ошибку, и вместо этого нам нужно сделать что-то вроде:

find /path/to/folder -name "filenamestart*" -type f -exec rm -f {} \;

Это работает, но очень медленно и постоянно не хватает памяти.

Есть лучший способ сделать это? В идеале я хотел бы удалить весь каталог, не заботясь о содержимом внутри него.

Тоби
источник
17
rm -rf *в папке, вероятно, происходит сбой из-за слишком большого количества аргументов; но что rm -rf folder/если вы все равно хотите удалить весь каталог?
2012 г.
4
Вместо того, чтобы удалять его вручную, я предлагаю разместить папку в отдельном разделе и просто размонтировать && format && remount.
bbaja42
7
Просто из любопытства - сколько файлов нужно, чтобы взломать rm -rf?
jw013
7
Возможно, вам следует переименовать вопрос в нечто более точное, например, «Эффективно удалить большой каталог, содержащий тысячи файлов». Чтобы удалить каталог и его содержимое, по определению необходима рекурсия. Вы можете вручную отсоединить только сам индексный каталог (вероятно, требуются права суперпользователя), размонтировать файловую систему и запустить fsckна ней, чтобы освободить неиспользуемые блоки диска, но такой подход кажется рискованным и, возможно, не будет быстрее. Кроме того, проверка файловой системы может в любом случае включать рекурсивный обход дерева файловой системы.
jw013
4
Когда у меня было ccacheтакое огромное файловое дерево, и оно rmзаняло так много времени (и замедлило всю систему), стало намного быстрее копировать все другие файлы из файловой системы, форматировать и копировать их обратно. С тех пор я даю таким огромным небольшим файловым деревьям их собственную выделенную файловую систему, так что вы можете mkfsнапрямую вместо rm.
frostschutz

Ответы:

213

Использование rsync удивительно быстро и просто.

mkdir empty_dir
rsync -a --delete empty_dir/    yourdirectory/

В ответе @ sarath упоминается еще один быстрый выбор: Perl! Его тесты быстрее, чем rsync -a --delete.

cd yourdirectory
perl -e 'for(<*>){((stat)[9]<(unlink))}'

Источники:

  1. https://stackoverflow.com/questions/1795370/unix-fast-remove-directory-for-cleaning-up-daily-builds
  2. http://www.slashroot.in/which-is-the-fastest-method-to-delete-files-in-linux
stevendaniels
источник
4
Спасибо, очень полезно. Я использую rsync все время, я понятия не имел, что вы можете использовать его для удаления, как это. Значительно быстрее, чем rm -rf
Джон Пауэлл
22
rsyncможет быть быстрее, чем обычный rm, потому что он гарантирует удаление в правильном порядке, поэтому требуется меньше пересчетов btress. Посмотрите этот ответ serverfault.com/a/328305/105902
Marki555
7
Может ли кто-нибудь изменить выражение perl для рекурсивного удаления всех каталогов и файлов внутри directory_to_be_deleted ?
Абхинав
5
Примечания: добавить -Pвозможность Rsync еще некоторое дисплея, а также, быть осторожными о синтаксисе, то замыкающие косые являются обязательными. Наконец, вы можете запустить команду rsync в первый раз с -nопцией сначала запустить пробный запуск .
Drasill
1
-aравняется -rlptgoD, но для удаления -rdнужно только
Koen.
39

Кто-то в Twitter предложил использовать -deleteвместо-exec rm -f{} \;

Это повысило эффективность команды, хотя она все еще использует рекурсию для прохождения всего.

Тоби
источник
11
Это нестандартно. У GNU findесть -deleteи другие, findможет быть.
энзотиб
14
-deleteвсегда следует отдавать предпочтение, -exec rmкогда это возможно, по соображениям безопасности и эффективности.
jw013
6
GNU является стандартом де-факто .
RonJohn
17

Как насчет чего-то вроде: find /path/to/folder -name "filenamestart*" -type f -print0 | xargs -0rn 20 rm -f

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

digital_infinity
источник
2
Вам, вероятно, не нужен -n 20бит, так как xargs все равно должен ограничивать себя приемлемыми размерами списка аргументов.
бесполезно
Да ты прав. Вот записка man xargs: (...) max-chars characters per command line (...). The largest allowed value is system-dependent, and is calculated as the argument length limit for exec. Так что -nопция предназначена для тех случаев, когда xargs не может определить размер буфера CLI или если у исполняемой команды есть некоторые ограничения.
digital_infinity
12

Расширяя один из комментариев, я не думаю, что вы делаете то, что, как вы думаете, вы делаете.

Сначала я создал огромное количество файлов, чтобы смоделировать вашу ситуацию:

$ mkdir foo
$ cd foo/
$ for X in $(seq 1 1000);do touch {1..1000}_$X; done

Затем я попробовал то, что я ожидал потерпеть неудачу, и как это звучит, как вы делаете в вопросе:

$ rm -r foo/*
bash: /bin/rm: Argument list too long

Но это делает работу:

$ rm -r foo/
$ ls foo
ls: cannot access foo: No such file or directory
Izkata
источник
6
Это единственное решение, которое сработало: запустить rm -Rf bigdirectoryнесколько раз. У меня был каталог с тысячами миллионов подкаталогов и файлов. Я даже не мог запустить lsили findили rsyncв этом каталоге, потому что он исчерпал память. Команда rm -Rfвыходила много раз (из памяти), удаляя только часть миллиардов файлов. Но после многих попыток он, наконец, сделал свою работу. Кажется, это единственное решение, если проблема заключается в нехватке памяти.
Эрик
12

Умный трюк:

rsync -a --delete empty/ your_folder/

Это супер интенсивно использует процессор, но действительно очень быстро. См. Https://web.archive.org/web/20130929001850/http://linuxnote.net/jianingy/en/linux/a-fast-way-to-remove-huge-number-of-files.html.

MZAweb
источник
Это не так быстро, потому что он читает содержимое каталога неэффективно. Смотрите этот ответ для 10x быстрее решения и объяснения serverfault.com/a/328305/105902
Marki555
2
@ Marki555: в редактировании вопроса сообщается, что 60 секунд rsync -a --deleteпротив 43 для lsdent. Отношение 10x было для time ls -1 | wc -l против time ./dentls bigfolder >out.txt(это частично справедливое сравнение из-за > fileпротив wc -l).
Хастур
Проблема в том , что существует NONE команд там на самом деле не делать требуемую операцию обхода для удаления. Код они дают? НЕ РАБОТАЕТ, как описано Marki555.
Svartalf
6

У меня была возможность проверить, -deleteпо сравнению с -exec rm \{\} \;и для меня -deleteбыл ответ на эту проблему.

Использование -deleteудаленных файлов в папке из 400 000 файлов как минимум в 1000 раз быстрее, чем rm.

Статья «Как удалить большое количество файлов в Linux» предполагает, что это примерно в три раза быстрее, но в моем тесте разница была гораздо более существенной.

user2365090
источник
3
Использование find -execвыполняет rmкоманду для каждого файла отдельно, поэтому она такая медленная.
Marki555
5

О -deleteпараметре выше: я использую его для удаления большого количества файлов (1M + est) во временной папке, которую я создал и случайно забыл очистить ночью. Я случайно заполнил свой диск / раздел, и ничто иное не могло удалить их, кроме find .команды. Это медленно, сначала я использовал:

find . -ls -exec rm {} \;

Но это заняло ЧРЕЗВЫЧАЙНОЕ количество времени. Примерно через 15 минут он начал удалять некоторые файлы, но я предполагаю, что он удалял менее 10 или около того в секунду после того, как он наконец запустился. Итак, я попробовал:

find . -delete

вместо этого, и я позволю этому бежать прямо сейчас. Кажется, он работает быстрее, хотя это ЧРЕЗВЫЧАЙНО увеличивает нагрузку на ЦП, чего не было у другой команды. Он работает уже около часа, и я думаю, что я снова получаю место на моем диске, и раздел постепенно «уменьшается», но это все еще занимает очень много времени. Я серьезно сомневаюсь, что он работает в 1000 раз быстрее, чем другие. Как и во всем, я просто хотел указать на компромисс между пространством и временем. Если у вас есть запасная пропускная способность ЦП (у нас есть), запустите последний. Мой процессор работает ( uptimeсообщает):

10:59:17 up 539 days, 21:21,  3 users,  load average: 22.98, 24.10, 22.87

И я видел, что средняя нагрузка превышает 30,00, что не хорошо для загруженной системы, но для нашей, которая обычно слегка загружена, это нормально в течение пары часов. Я проверил большинство других вещей в системе, и они все еще отзывчивы, так что пока все в порядке.

Скотти
источник
если вы собираетесь использовать, execвы почти наверняка захотите не использовать, -lsа do find . -type f -exec rm '{}' ++ быстрее, потому что он даст rm столько аргументов, сколько он может обработать одновременно.
ксенотеррацид
Я думаю, что вы должны пойти дальше и отредактировать это в своем собственном ответе ... это действительно слишком долго для комментария. Кроме того, похоже, что ваша файловая система имеет довольно дорогие удаления, интересно, какая это? Вы можете пройти find … -deleteчерез это niceили ionice, это может помочь. Так что может изменить некоторые параметры монтирования на менее безопасные настройки. (И, конечно, в зависимости от того, что еще находится в файловой системе, самый быстрый способ удаления всего часто mkfs.)
Дероберт
3
Средняя нагрузка - это не всегда процессор, это всего лишь показатель количества заблокированных процессов с течением времени. Процессы могут блокировать дисковый ввод-вывод, что, скорее всего, происходит здесь.
Score_Under
Также обратите внимание, что средняя загрузка не учитывает количество логических процессоров. Таким образом, loadavg 1для одноядерного компьютера - это то же самое, что и loadavg 64для 64-ядерного компьютера. Это означает, что каждый процессор занят 100% времени
Marki555
3

Есть несколько методов, которые можно использовать для удаления большого количества файлов в Linux. Вы можете использовать команду find with delete, которая работает быстрее, чем опция exec. Тогда вы можете использовать perl unlink, затем даже rsync. Как удалить большое количество файлов в Linux

Сарат
источник
3

Подумайте об использовании тома Btrfs и просто удалите весь том для такого каталога с большим количеством файлов.

В качестве альтернативы вы можете создать файл образа FS, затем размонтировать и удалить его, чтобы действительно быстро все удалить.

Сергей
источник
2

Предполагая, что GNU parallelустановлен, я использовал это:

parallel rm -rf dir/{} ::: `ls -f dir/`

и это было достаточно быстро.

Начо
источник
2

Используйте rm -rf directoryвместо rm -rf *.

Первоначально мы делали, rm -rf *находясь в каталоге, чтобы очистить содержимое и думали, что это было настолько быстро, насколько это возможно. Но затем один из наших старших инженеров предложил нам избегать использования звездочек ( *) и вместо этого передавать в родительский каталог, например rm -rf directory.

После некоторых серьезных дискуссий о том, как это не будет иметь значения, мы решили сравнить его с третьим методом использования find. Вот результаты:

time rm -rf *                   2m17.32s
time rm -rf directory           0m15.60s
time find directory -delete     0m16.97s

rm -rf directoryпримерно в 9 раз быстрее, чем rm -rf *!

Само собой разумеется, мы купили тому инженеру пиво!

Так что теперь мы используем, rm -rf directory; mkdir directoryчтобы удалить каталог и заново создать его.

Джошуа Пинтер
источник
1

Как я узнал на этом сайте , для удаления каталогов REALLY LARGE необходим другой подход - вам нужно будет использовать ionice.It гарантирует (с -c3), что удаление будет выполняться только тогда, когда у системы есть время IO для этого. Нагрузка на ваши системы не возрастет до высокого уровня, и все останется отзывчивым (хотя время, затрачиваемое на поиск процессора, было довольно высоким - около 50%)

find <dir> -type f -exec ionice -c3 rm {} \;
гамма
источник
5
использование +вместо \;сделало бы это быстрее, так как он передает больше аргументов rm одновременно, меньше разветвляется
xenoterracide
1
Почему бы нет ionice -c3 find <dir> -type f -delete
JTGD
1

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

Файл nice_delete:

#!/bin/bash

MAX_LOAD=3
FILES=("$@")
BATCH=100

while [ ${#FILES[@]} -gt 0 ]; do
    DEL=("${FILES[@]:0:$BATCH}")
    ionice -c3 rm "${DEL[@]}"
    echo -n "#"
    FILES=("${FILES[@]:$BATCH}")
    while [[ $(cat /proc/loadavg | awk '{print int($1)}') -gt $MAX_LOAD ]]; do
        echo -n "."
        sleep 1
    done
done

А теперь удалите файлы:

find /path/to/folder -type f -exec ./nice_delete {} \+

Find создаст пакеты (см. getconf ARG_MAX) Из нескольких десятков тысяч файлов и передаст их nice_delete. Это создаст еще меньшие партии, чтобы позволить спать, когда обнаружена перегрузка.

brablc
источник
0
ls -1 | xargs rm -rf 

должен работать внутри главной папки

PsyStyle
источник
1
lsне будет работать из-за количества файлов в папке. Вот почему я должен был использовать find, хотя спасибо.
Тоби
4
@Toby: попробуйте ls -f, что отключает сортировку. Сортировка требует, чтобы весь каталог был загружен в память для сортировки. Несортированный lsдолжен иметь возможность передавать свои выходные данные.
Camh
1
Не работает с именами файлов, которые содержат переводы строк.
maxschlepzig
@ камера, это правда. Но удаление файлов в отсортированном порядке происходит быстрее, чем в несортированном (из-за пересчета btree каталога после каждого удаления). Смотрите этот ответ для примера serverfault.com/a/328305/105902
Marki555
@maxschlepzig для таких файлов, которые вы можете использовать find . -print0 | xargs -0 rm, которые будут использовать NULL char в качестве разделителя имени файла.
Marki555
0

Для подсказки Изкаты выше:

Но это делает работу:

$ rm -r foo/
$ ls foo
ls: cannot access foo: No such file or directory

Это почти сработало - или сработало бы - но у меня были некоторые проблемы с разрешением; файлы были на сервере, но я все еще не понимаю, откуда возникла проблема с разрешениями. В любом случае, Терминал запросил подтверждение для каждого файла. Количество файлов было около 20 000, так что это не вариант. После «-r» я добавил опцию «-f», поэтому вся команда была « rm -r -f foldername / ». Тогда это, казалось, работало нормально. Я новичок в Терминале, но я думаю, что все в порядке, верно? Спасибо!

user41527
источник
0

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

$ shred -zuv folder

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

mv folder folder_del
mkdir folder
rm -rf folder_del

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

Полемон
источник
1
shred не будет работать со многими современными файловыми системами.
0

Если вы просто хотите избавиться от множества файлов как можно быстрее, это ls -f1 /path/to/folder/with/many/files/ | xargs rmможет сработать, но лучше не запускать его на производственных системах, поскольку ваша система может стать причиной проблем с вводом-выводом, а приложения могут застрять во время операции удаления.

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

#!/bin/bash

# Path to folder with many files
FOLDER="/path/to/folder/with/many/files"

# Temporary file to store file names
FILE_FILENAMES="/tmp/filenames"

if [ -z "$FOLDER" ]; then
    echo "Prevented you from deleting everything! Correct your FOLDER variable!"
    exit 1
fi

while true; do
    FILES=$(ls -f1 $FOLDER | wc -l)
    if [ "$FILES" -gt 10000 ]; then
        printf "[%s] %s files found. going on with removing\n" "$(date)" "$FILES"
        # Create new list of files
        ls -f1 $FOLDER | head -n 5002 | tail -n 5000 > "$FILE_FILENAMES"

        if [ -s $FILE_FILENAMES ]; then
            while read FILE; do
                rm "$FOLDER/$FILE"
                sleep 0.005
            done < "$FILE_FILENAMES"
        fi
    else
        printf "[%s] script has finished, almost all files have been deleted" "$(date)"
        break
    fi
    sleep 5
done
Леон Крамер
источник