Рекурсивное измерение использования диска определенными типами файлов для каждого каталога (в качестве демонстрации для 'du --include')

find. -type d -printf "%d %p\n" |\ sort -nr |\ perl -pe 's/^\d+\s//;' |\ while read dir; do \ (rmdir "$dir" > /dev/null 2>&1); \ done

Вот как это работает:

  1. Рекурсивный список всех каталогов вместе с их глубиной
  2. Сортировка по убыванию их глубины
  3. Отфильтровать только пути к каталогам
  4. Запустите rmdirв списке один за другим
7
05.07.2020, 16:32
3 ответа

Чтобы подсчитать использование диска, а не сумму видимого размера, вам нужно использовать %b¹ вместо %sи убедиться, что каждый файл подсчитывается только один раз, поэтому что-то вроде:

LC_ALL=C find. -iname '*.py' -type f -printf '%D:%i\0%b\0%h\0' |
  gawk -v 'RS=\0' -v OFS='\t' -v max=50 '
    {
      inum = $0
      getline du
      getline dir
    }
    ! seen[inum]++ {
      gsub(/\\/, "&&", dir)
      gsub(/\n/, "\\n", dir)
      sum[dir] += du
    }
    END {
      n = 0
      PROCINFO["sorted_in"] = "@val_num_desc"
      for (dir in sum) {
        print sum[dir] * 512, dir
        if (++n >= max) break
      }
    }' | numfmt --to=iec-i --suffix=B --delimiter=$'\t'

Новые строки в именах каталогов отображаются как \n, а обратные косые черты (по крайней мере декодируются как таковые в текущей локали² )как \\.

Если файл найден более чем в одном каталоге, он засчитывается относительно первого найденного в (порядке, не являющемся детерминированным ).

Предполагается, что в среде нет POSIXLY_CORRECTпеременной (, если она есть, установка PROCINFO["sorted_in"]не влияет на gawk, поэтому список не будет сортироваться ). Если вы не можете этого гарантировать³, вы всегда можете запустить gawkкак env -u POSIXLY_CORRECT gawk...(, предполагая GNU envили совместимый; или(unset -v POSIXLT_CORRECT; gawk...)).

Несколько других проблем с вашим подходом:

  • без LC_ALL=CGNU findне сообщал бы о файлах, имена которых не образуют допустимых символов в локали, так что вы могли пропустить некоторые файлы.
  • Встраивание {}в код shпредставляло собой уязвимость, связанную с внедрением произвольного кода. Подумайте, например, о файле с именем $(reboot).py. Вы никогда не должны этого делать, пути к файлам должны передаваться как дополнительные аргументы и ссылаться в коде с использованием позиционных параметров.
  • echoнельзя использовать для отображения произвольных данных (, особенно с -e, что здесь не имеет смысла ). Вместо этого используйте printf.
  • С помощью xargs -r0 du -schduможет вызываться несколько раз, если список файлов большой, и в этом случае последняя строка будет включать только итоговую сумму за последний запуск.

¹ %bсообщает об использовании диска в единицах по 512 -байт. 512 байт — это минимальная степень детализации для выделения диска, поскольку это размер традиционного сектора. Также есть %k, то есть int(%b / 2),но это дало бы неправильные результаты в файловых системах с блоками по 512 байт (блоки файловой системы обычно имеют степень 2 и имеют размер не менее 512 байт)

² Использование LC_ALL=Cдля gawk также сделало бы его немного более эффективным, но, возможно, исказило бы вывод в локалях с использованием кодировок BIG5 или GB18030 (, а имена файлов также закодированы в этом наборе символов )как кодировка обратной косой черты также встречается в кодировке некоторых других символов.

³ Учтите, что если ваш shравен bash, POSIXLY_CORRECTустановлен в yв сценариях shи экспортируется в среду, если shзапускается с -aили -o allexport, так что эта переменная тоже может появиться непреднамеренно.

6
18.03.2021, 23:23

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

.
find. -type f -iname '*.py' -printf '%s %h\0' |
    awk 'BEGIN { RS="\0"; }; '\
'{ pos=index($0," "); size=substr($0,1,(pos-1)); dir=substr($0,pos+1); gsub("\n","\\n",dir); '\
'if(dir!=lastdir) { if(NR>1) { "numfmt --to=iec-i --suffix=B " sizesum " | tr -d \"\n\"" | getline fsize; print fsize " " lastdir; } '\
'sizesum=size; lastdir=dir; } '\
'else sizesum=sizesum+size; }; '\
'END { "numfmt --to=iec-i --suffix=B " sizesum " | tr -d \"\n\"" | getline fsize; print fsize " " lastdir; }'

3,2KiB./dir1
1,1MiB./dir2

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

2
18.03.2021, 23:23

Я подозреваю, что вам нужно написать свой собственный du.

В настоящее время вы выполняете тройную рекурсию по иерархии, используя два find и du.

Я бы посоветовал начать с пакета Perl File::Find.

В качестве альтернативы, ваша первая находка может быть выведена с чем-то вроде -printf '%k %h\n', а затем вы можете отсортировать по каталогу, использовать perl или awk (или даже bash ), чтобы суммировать каталоги и преобразовать в «человеческий» читаемый, и наконец, сортировка и голова.

В любом случае вам следует A )пройтись по дереву каталогов только один раз, а B )создать как можно меньше процессов.

Редактировать :Пример реализации

#!/bin/bash

find. -type f -iname '*.py' -printf '%k %h\n' | sort -k2 | (
    at=
    bt=
    output() {
        if [[ -n "$at" ]]
        then
            printf '%s\t%s\n' "$at" "$bt"
        fi
    }
    while read a b
    do
        if [[ "$b" != "$bt" ]]
        then
            output
            bt="$b"
            at=0
        fi
        at=$(( $at + $a ))
    done
    output
) | sort -hr | head -50 | numfmt -d'   ' --field=1 --from-unit=Ki --to=iec-i

Примечание. :%k важен. %s сообщает видимый размер,в то время как %k (иdu)сообщают о размере диска. Они отличаются для разреженных файлов и больших файлов. (Хочешь du --apparent-size, пусть будет так.)

Примечание. :numfmt должен идти в конце, поэтому он запускается один раз. Используя '%k', необходимо указать единицу измерения из -.

Примечание. :Параметр -d numfmt должен содержать одну вкладку. Я не могу ввести это здесь, и numfmt не примет -d'\t'. Если разделитель не является табуляцией, интервалы искажаются. Таким образом, я использовал printf вместо эха в основном теле. (В качестве альтернативы можно было бы использовать echo и final sed для замены первого пробела на табуляцию.

Примечание. :Сначала я пропустил первую сортировку и получил повторяющиеся записи для некоторых каталогов при повторном -тестировании.

Примечание. :numfmt появился относительно недавно.

3
18.03.2021, 23:23

Теги

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