Znajdowanie rzadkich plików?

19

Czy istnieje prosty sposób na znalezienie wszystkich rzadkich plików w moim systemie lub w określonym drzewie katalogów?

Jeśli ma to znaczenie, używam zshna Ubuntu 12.04, chociaż bardziej ogólna odpowiedź na Unix-y dla bash / sh, na przykład, byłaby w porządku.

Edycja : aby wyjaśnić, szukam rzadkich plików, a nie sprawdzam rzadkości statusu pojedynczego pliku.

Andrew Ferrier
źródło
2
Co powoduje, że wyszukiwanie rzadkich plików nie wiąże się ze sprawdzaniem rzadkości poszczególnych plików?
jlliagre

Odpowiedzi:

11

W systemach (i systemach plików) obsługujących SEEK_HOLE lseekflagę (takich jak Ubuntu 12.04 na ext4) i przyjmujących, że wartość SEEK_HOLEwynosi 4 tak jak w Linuksie:

if perl -le 'seek STDIN,0,4;$p=tell STDIN;
   seek STDIN,0,2; exit 1 if $p == tell STDIN'< the-file; then
  echo the-file is sparse
else
  echo the-file is not sparse
fi

Ta składnia powłoki to POSIX. Non-przenośny rzeczy są w niej perli że SEEK_HOLE.

lseek(SEEK_HOLE)szuka początku pierwszego otworu w pliku lub końca pliku, jeśli nie znaleziono otworu. Powyżej wiemy, że plik nie jest rzadki, gdy lseek(SEEK_HOLE)przenosi nas na koniec pliku (w to samo miejsce, co lseek(SEEK_END)).

Jeśli chcesz wyświetlić listę rzadkich plików:

find . -type f ! -size 0 -exec perl -le 'for(@ARGV){open(A,"<",$_)or
  next;seek A,0,4;$p=tell A;seek A,0,2;print if$p!=tell A;close A}' {} +

GNU find(od wersji 4.3.3) musi -printf %Szgłaszać rzadkość pliku. Przyjmuje to samo podejście, co odpowiedź frostschutza , ponieważ przyjmuje stosunek wykorzystania dysku do wielkości pliku, więc nie ma gwarancji zgłaszania wszystkich plików rzadkich (na przykład, gdy istnieje kompresja na poziomie systemu plików lub gdy miejsce zaoszczędzone przez dziury nie kompensować narzut infrastruktury infrastruktury plików lub duże rozszerzone atrybuty), ale działałby na systemach, które nie mają SEEK_HOLEsystemu plików lub na systemach, które nie są SEEK_HOLEzaimplementowane. Tutaj z narzędziami GNU:

find . -type f ! -size 0 -printf '%S:%p\0' |
  awk -v RS='\0' -F : '$1 < 1 {sub(/^[^:]*:/, ""); print}'

(zauważ, że wcześniejsza wersja tej odpowiedzi nie działała poprawnie po findwyrażeniu rzadkości, jak na przykład 3.2e-05. Dzięki odpowiedzi @ flashydave za zwrócenie jej uwagi)

Stéphane Chazelas
źródło
Taki sam komentarz jak powyżej; Szukam sposobu na znalezienie wszystkich rzadkich plików, a nie sprawdzanie konkretnego pliku.
Andrew Ferrier,
1
Może findpowinien również wykluczyć pliki 0-bajtowe?
frostschutz
@frostschutz, dobry punkt, odpowiedź zaktualizowana.
Stéphane Chazelas,
Niezłe znalezisko z find -printf '%S'! :-)
frostschutz
1
@Brian, zamień trpolecenie naxargs -r0 rm -f
Stéphane Chazelas
8

Plik jest zwykle rzadki, gdy liczba przydzielonych bloków jest mniejsza niż rozmiar pliku (tutaj przy użyciu GNU takiego statjak w Ubuntu, ale uważaj, inne systemy mogą mieć niezgodne implementacje stat).

if [ "$((`stat -c '%b*%B-%s' -- "$file"`))" -lt 0 ]
then
    echo "$file" is sparse
else
    echo "$file" is not sparse
fi

Wariant z find: (skradziony ze Stephane)

find . -type f ! -size 0 -exec bash -c '
    for f do
        [ "$((`stat -c "%b*%B-%s" -- "$f"`))" -lt 0 ] && printf "%s\n" "$f";
    done' {} +

Zwykle umieszczasz to w skrypcie powłoki, a następnie uruchamiasz skrypt powłoki.

find . -type f ! -size 0 -exec ./sparsetest.sh {} +
frostschutz
źródło
Może to nie działać, jeśli rzadkie bloki nie są wystarczające, aby pokryć narzut pośrednich bloków, na przykład w tradycyjnych systemach plików, lub jeśli kompresja zamiast rzadkości zmniejsza ilość przydzielonego miejsca.
Stéphane Chazelas,
Pewnie; SEEK_HOLEjest jednak równie problematyczny, ponieważ nie jest obsługiwany przez wiele platform / systemów plików. W Linuksie możesz także użyć FIEMAP/ FIBMAP, ale FIBMAPw szczególności jest on strasznie wolny ... po prostu nie wydaje się, aby był to dobry sposób.
frostschutz
Wiele z tych metod wymaga najpierw synchronizacji pliku.
frostschutz
Dzięki. To jednak tak naprawdę nie odpowiada na pytanie. Nie chcę sprawdzać, czy dany plik jest rzadki, ale znaleźć wszystkie rzadkie pliki w systemie.
Andrew Ferrier,
1
@AndrewFerrier przepraszam, myślę, że myślałem, że to wystarczająco trywialne, aby zawinąć to w for file in *lub find. Jeśli możesz przetestować pojedynczy plik, możesz przetestować wszystkie pliki ... chociaż musisz wykluczyć katalogi za pomocą tej metody.
frostschutz
3

Odpowiedź Stephane'a Chazelasa powyżej nie bierze pod uwagę faktu, że niektóre rzadkie pliki z parametrem znajdź% S zgłaszają stosunek jako liczby zmiennoprzecinkowe, takie jak

9.31323e-09:./somedir/sparsefile.bin

Można je znaleźć dodatkowo z

find . -type f ! -size 0 -printf '%S:%p\0' |
   sed -zn '/^\(0[^:]*:\)\|\([0-9.]\+e-.*:\)/p' |
   tr '\0' '\n'
flashydave
źródło
1

Krótki skrypt, który napisałem, próbując dowiedzieć się, jakie są lokalizacje dziur w pliku:

#!/usr/bin/python3
import os
import sys
import errno

def report(fname):
    fd = os.open(fname, os.O_RDONLY)
    len = os.lseek(fd, 0, os.SEEK_END)
    offset = 0
    while offset < len:
        start = os.lseek(fd, offset, os.SEEK_HOLE)
        if start == len:
            break
        try:
            offset = os.lseek(fd, start, os.SEEK_DATA)
        except OSError as e:
            if e.errno == errno.ENXIO:
                offset = len
            else:
                raise
        print(f'found hole between 0x{start:08X} and 0x{offset:08X} ({offset - start} bytes)')

if __name__ == '__main__':
    for name in sys.argv[1:]:
        report(name)

To drukuje takie rzeczy jak:

$ echo -n 'a' >zeros; truncate -s $((4096*4)) zeros; test/report-holes.py zeros
found hole between 0x00001000 and 0x00004000 (12288 bytes)
zbyszek
źródło
Nie odpowiada na moje pytanie, ponieważ szukałem rzadkich plików, nie dziur w konkretnym pliku, ale wciąż przydatny / odpowiedni skrypt. Dzięki. Pozytywne.
Andrew Ferrier,