Нахождение разреженных файлов?

19

Есть ли простой способ найти все разреженные файлы в моей системе или в определенном дереве каталогов?

Если это уместно, я использую zshв Ubuntu 12.04, хотя более общий Unix-y ответ для bash / sh, например, был бы в порядке.

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

Эндрю Ферье
источник
1
Связанный: Подробная информация о редких файлах в Linux
Стефан Шазелас
2
Что заставляет вас чувствовать, что поиск редких файлов не включает проверку статуса разреженности отдельных файлов?
jlliagre

Ответы:

11

В системах (и файловых системах), поддерживающих SEEK_HOLE lseekфлаг (как, например, ваша Ubuntu 12.04 на ext4) и предполагающих значение для SEEK_HOLE4, как в Linux:

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

Этот синтаксис оболочки - POSIX. Непереносимые вещи в нем есть perlи то SEEK_HOLE.

lseek(SEEK_HOLE)ищет начало первого отверстия в файле или конец файла, если отверстие не найдено. Выше мы знаем, что файл не редкий, когда он lseek(SEEK_HOLE)переносит нас в конец файла (в то же место, что и lseek(SEEK_END)).

Если вы хотите перечислить разреженные файлы:

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(начиная с версии 4.3.3) должен -printf %Sсообщать о редкости файла. Он использует тот же подход, что и ответ frostschutz, в том смысле, что он принимает соотношение использования диска и размера файла, поэтому не гарантируется, что он сообщает обо всех разреженных файлах (например, когда есть сжатие на уровне файловой системы или когда пространство, сэкономленное дырами, не компенсировать накладные расходы на инфраструктуру файловой системы или большие расширенные атрибуты), но будет работать в системах, которые не имеют, SEEK_HOLEили в файловых системах, где SEEK_HOLEэто не реализовано. Здесь с инструментами GNU:

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

(обратите внимание, что более ранняя версия этого ответа не работала должным образом, когда findвыражалась редкость, как, например, 3.2e-05. Спасибо ответу @ flashydave за то, что он привлек мое внимание)

Стефан Шазелас
источник
Тот же комментарий, что и выше; Я ищу способ найти все разреженные файлы, а не проверять определенный файл.
Эндрю Ферье
1
Может быть, findследует также полностью исключить 0-байтовые файлы?
frostschutz
@frostschutz, хорошая точка зрения, ответ обновлен.
Стефан Шазелас
Приятно найти с find -printf '%S'! :-)
frostschutz
1
@ Брайан, заменить trкоманду сxargs -r0 rm -f
Stéphane Chazelas
8

Файл обычно разрежен, когда количество выделенных блоков меньше размера файла (здесь используется GNU, statкак в Ubuntu, но следует помнить, что другие системы могут иметь несовместимые реализации stat).

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

Вариант с find: (украдено у Стефана)

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

Вместо этого вы обычно помещаете это в сценарий оболочки, а затем исполняете сценарий оболочки.

find . -type f ! -size 0 -exec ./sparsetest.sh {} +
frostschutz
источник
Это может не сработать, если разреженных блоков недостаточно, чтобы покрыть накладные расходы косвенных блоков, например, в традиционных файловых системах, или если сжатие вместо разрежения уменьшает объем выделенного пространства.
Стефан Шазелас
Конечно; SEEK_HOLEЭто так же проблематично, поскольку не поддерживается многими платформами / файловыми системами. В Linux вы также можете использовать FIEMAP/ FIBMAP, но, FIBMAPв частности, это ужасно медленно ... просто не кажется, что это хороший способ.
frostschutz
Также многие из этих методов требуют, чтобы файл был синхронизирован в первую очередь.
frostschutz
Благодарю. Это действительно не отвечает на вопрос, хотя. Я не смотрю, чтобы проверить, является ли определенный файл разреженным, но чтобы найти все разреженные файлы в системе.
Эндрю Ферье
1
@AndrewFerrier извините, я думаю, я думал, что это достаточно тривиально, чтобы обернуть это в for file in *или find. Если вы можете протестировать один файл, вы можете протестировать все файлы ... хотя вы должны исключить каталоги с помощью этого метода.
frostschutz
3

Ответ Stephane Chazelas выше не учитывает тот факт, что некоторые разреженные файлы с параметром find% S сообщают об этом соотношении в виде чисел с плавающей запятой, таких как

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

Они могут быть найдены в дополнение к

find . -type f ! -size 0 -printf '%S:%p\0' |
   sed -zn '/^\(0[^:]*:\)\|\([0-9.]\+e-.*:\)/p' |
   tr '\0' '\n'
flashydave
источник
1

Короткий сценарий, который я написал, пытаясь выяснить, каковы расположения дырок в файле:

#!/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)

Это печатает такие вещи, как:

$ echo -n 'a' >zeros; truncate -s $((4096*4)) zeros; test/report-holes.py zeros
found hole between 0x00001000 and 0x00004000 (12288 bytes)
Zbyszek
источник
Не отвечает на мой вопрос, так как я искал разреженные файлы, не дыры в конкретном файле, но все же полезный / релевантный скрипт. Благодарю. Upvoted.
Эндрю Ферье