Zliczanie zajętości miejsca katalogu (+ pliki rekurencyjnie) z wykresem

Mawiają – potrzeba matką wynalazku. Tak też jest i teraz.

Zaczynając jednak od początku… Kilka miesięcy temu Borys polecił mi narzędzie, którego nazwa to nnn. W dużym uproszczeniu jest to bardzo prosty menadżer plików. Od polecenia przez niego, używam tego narzędzia namiętnie. Szczególnie, gdy potrzebuję usunąć więcej plików, po różnych katalogach, lub przekopiować coś na szybko. Poruszanie się z pomocą strzałek działa znakomicie. Jestem bardzo zadowolony z niego.

Posiada jednak pewne ograniczenia, mimo przełącznika d, który pozwala na wylistowanie plików z rozmiarami – nie potrafi przeliczyć zajętości katalogów. A nie ukrywam, że często tego potrzebuję. Szczególnie na serwerze, lub na serwerach klientów (gdzie niestety tego narzędzia nie mamy zainstalowanego, ale o tym dalej).

Zrzut ekranu z programu nnn z listą plików.

Szukałem alternatywy, która wyliczałaby zajętość plików i folderów. Znalazłem ncdu.

ncdu potrafi z kolei fajnie w postaci „wykresu” przedstawić aktualną zajętość katalogu, w którym się znajdujemy, oraz udostępnia podstawowe funkcje menadżera plików, jak np. usuwanie pojedynczych plików/folderów, które mamy zaznaczone.

Zrzut ekranu z ncdu

Generuje natomiast pewne ograniczenie, którego nie byłem w stanie przeskoczyć – program nie jest zainstalowany na serwerach, z których korzystam, więc jest, ale bez dostępu. Poza tym brakowało w nim też fundamentalnej dla mnie funkcji – przeliczania liczby plików z obecnego katalogu oraz podkatalogów.

No i tutaj dochodzimy do finału wpisu. Ze względu na ograniczenia, stawiane przez sysops oraz własną wygodę, zrobiłem swój skrypcik, który robi to, co chcę, od początku do końca :)

  1. Sortuje pliki i katalogi uwzględniając ich rozmiar
  2. Pokazuje to w czytelnej postaci wykresu
  3. Zlicza liczbę plików i katalogów
  4. Nie wymaga instalacji ;)
#!/usr/bin/env bash
#
# dusize.sh - wizualizacja zajętości katalogu przy użyciu du + awk.
# Sortuje wpisy malejąco wg rozmiaru i rysuje słupki proporcjonalne
# do największego elementu. Katalogi na niebiesko, pliki na bialo.
# Uruchomienie: ./dusize.sh   działa w bieżącym katalogu.
# Kontakt: Filip Cierpich (keepmind.eu)
export LC_ALL=C
spin='-\|/'
si=0
tick() {
    [ -t 2 ] || return
    si=$(( (si + 1) % 4 ))
    printf '\r\033[1;36m%s\033[0m Skanowanie: %-40.40s' "${spin:$si:1}" "$1" >&2
}
clear_line() { [ -t 2 ] && printf '\r\033[K' >&2; }
data=$(find . -mindepth 1 -maxdepth 1 -print0 2>/dev/null | while IFS= read -r -d '' path; do
    name=${path#./}
    tick "$name"
    size=$(du -sh -- "$path" 2>/dev/null | cut -f1)
    [ -n "$size" ] || continue
    if [ -d "$path" ]; then
        type=d
        # liczymy WSZYSTKIE pliki i podkatalogi w glab (bez ograniczenia poziomow)
        fc=$(find "$path" -mindepth 1 \( -type f -o -type d \) 2>/dev/null | wc -l); fc=$((fc))
    else
        type=f
        fc=-1
    fi
    printf '%s\t%s\t%s\t%s\n' "$size" "$type" "$fc" "$name"
done)
tick "podsumowanie"
# pelna rekurencja: osobno pliki i katalogi w calym drzewie
total_files=$(find . -mindepth 1 -type f 2>/dev/null | wc -l); total_files=$((total_files))
total_dirs=$(find . -mindepth 1 -type d 2>/dev/null | wc -l); total_dirs=$((total_dirs))
total=$((total_files + total_dirs))
dirsize=$(du -sh . 2>/dev/null | cut -f1)
clear_line
render() {
    printf '%s\n' "$data" | awk -F'\t' \
        -v total="$total" -v tfiles="$total_files" -v tdirs="$total_dirs" \
        -v dirsize="$dirsize" '
    BEGIN {
        BLUE="\033[1;34m"; WHITE="\033[0;37m"; RESET="\033[0m"
    }
    NF >= 4 {
        size=$1; type=$2; fc=$3; name=$4
        unit=substr(size, length(size))
        val=substr(size, 1, length(size)-1)
        if      (unit=="T") mb=val*1024*1024*1024
        else if (unit=="G") mb=val*1024*1024
        else if (unit=="M") mb=val*1024
        else if (unit=="K") mb=val+0
        else                mb=(val+0)/1024
        count++
        sizes[count]=mb; names[count]=name; labels[count]=size
        types[count]=type; fcounts[count]=fc
        if (type=="d") dirs++; else files++
    }
    END {
        for (i=1;i<=count;i++)
            for (j=i+1;j<=count;j++)
                if (sizes[j]>sizes[i]) {
                    t=sizes[i];   sizes[i]=sizes[j];     sizes[j]=t
                    t=names[i];   names[i]=names[j];     names[j]=t
                    t=labels[i];  labels[i]=labels[j];   labels[j]=t
                    t=types[i];   types[i]=types[j];     types[j]=t
                    t=fcounts[i]; fcounts[i]=fcounts[j]; fcounts[j]=t
                }
        printf "%-8s  %-6s  %-34s  %s\n", "Rozmiar","Wpisy","Wykres","Nazwa"
        print  "--------  ------  ----------------------------------  --------"
        maxmb=(count>0 && sizes[1]>0) ? sizes[1] : 1
        for (i=1;i<=count;i++) {
            bars=int((sizes[i]/maxmb)*32); if(bars<1)bars=1
            bar=""
            for(k=0;k<bars;k++)  bar=bar"\xe2\x96\x88"
            for(k=bars;k<32;k++) bar=bar"\xe2\x96\x91"
            color=(types[i]=="d") ? BLUE : WHITE
            fcl=(fcounts[i]=="-1") ? "-" : fcounts[i]
            printf "%s%-8s  %-6s  [%s]  %s%s\n", color, labels[i], fcl, bar, names[i], RESET
        }
        print  "--------  ------  ----------------------------------  --------"
        printf "Katalogi: %-4d  Pliki: %-4d\n", dirs+0, files+0
        printf "Zawartość (rekurencyjnie) -> Pliki: %d  Katalogi: %d  Razem (inody): %d\n", tfiles+0, tdirs+0,
total+0
        printf "Zajęta przestrzeń: %s\n", dirsize
    }'
}
if [ -t 1 ] && [ "${REVEAL:-1}" != 0 ]; then
    render | while IFS= read -r line; do
        printf '%s\n' "$line"
        sleep 0.015 2>/dev/null
    done
else
    render
fi

Uruchomić go możemy na kilka sposobów. Począwszy od bezpośredniego wpisania całego skryptu, jako polecenie, jako zapisanie pliku do katalogu ~/bin, z uprawnieniami +x, czy też globalnie wrzucając do /usr/bin. Jest też możliwość wrzucenia na swój hosting i wykonanie polecenia do jego uruchomienia, np.:

curl -sSL https://domena.pl/plik | bash

Komentarze

2 odpowiedzi na “Zliczanie zajętości miejsca katalogu (+ pliki rekurencyjnie) z wykresem”

  1. Przepiękny skrypt! Ja do tej pory korzystałem z takiego potoku:
    du -h –exclude=pCloudDrive | sort -rh | less
    który zaliasowałem sobie od komendy „sizes”, ale Twoje „dusize” jest o niebo elegantsze. Będe używał!

    1. Do tej pory na potrzeby własne w pracy korzystałem też z kombinacji `du -sh`, ale to nie było „to” :). A teraz mam „to” ;). Niech służy, smacznego!

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Wymagane pola są oznaczone *