Encontrando arquivos esparsos?

17

Existe uma maneira simples de encontrar todos os arquivos esparsos no meu sistema ou em uma árvore de diretórios específica?

Se for relevante, estou usando zsh no Ubuntu 12.04, embora uma resposta Unix-y mais genérica para o bash / sh, por exemplo, seja adequada.

Editar : para esclarecer, estou procurando pesquisar arquivos esparsos e não verificar o status de dispersão de um único.

    
por Andrew Ferrier 12.08.2013 / 17:09

4 respostas

9

Em sistemas (e sistemas de arquivos) suportando o sinal SEEK_HOLE lseek (como seu Ubuntu 12.04 no ext4) (e assumindo que o valor de SEEK_HOLE é 4 como no 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

Essa sintaxe do shell é POSIX. As coisas não portáveis são perl e SEEK_HOLE .

lseek(SEEK_HOLE) procura o início do primeiro buraco no arquivo ou o final do arquivo, se nenhum furo for encontrado. Acima sabemos que o arquivo não é esparso quando o lseek(SEEK_HOLE) nos leva ao final do arquivo (para o mesmo lugar que lseek(SEEK_END) ).

Se você deseja listar os arquivos esparsos:

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}' {} +

O% GNUfind (desde a versão 4.3.3) tem -printf %S para relatar o sparseness de um arquivo. Ele adota a mesma abordagem da frostschutz 'resposta , pois leva a proporção entre o uso do disco e o tamanho do arquivo. Não é garantido informar todos os arquivos esparsos (como quando há compactação no nível do sistema de arquivos ou onde o espaço salvo pelos furos não compensa a sobrecarga da infraestrutura do sistema de arquivos ou grandes atributos estendidos), mas funcionaria em sistemas que não possuem SEEK_HOLE ou sistemas de arquivos em que SEEK_HOLE não está implementado. Aqui com ferramentas GNU:

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

(observe que uma versão anterior dessa resposta não funcionou corretamente quando find expressou a dispersão como, por exemplo, 3.2e-05. Graças a @ flashydave responder por chamar minha atenção para isso

    
por 12.08.2013 / 17:38
6

Um arquivo é geralmente esparso quando o número de blocos alocados é menor que o tamanho do arquivo (aqui usando o GNU stat como encontrado no Ubuntu, mas cuidado, pois outros sistemas podem ter implementações incompatíveis de stat ).

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

Variante com find : (roubada de 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' {} +

Você normalmente colocaria isso em um script de shell e executaria o script de shell.

find . -type f ! -size 0 -exec ./sparsetest.sh {} +
    
por 12.08.2013 / 17:55
3

A resposta de Stephane Chazelas acima não leva em consideração o fato de que alguns arquivos esparsos com o parâmetro find% S relatam a proporção como números de ponto flutuante como

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

Estes podem ser encontrados em adição com

find . -type f ! -size 0 -printf '%S:%p
9.31323e-09:./somedir/sparsefile.bin
' | sed -zn '/^\(0[^:]*:\)\|\([0-9.]\+e-.*:\)/p' | tr '
find . -type f ! -size 0 -printf '%S:%p%pre%' |
   sed -zn '/^\(0[^:]*:\)\|\([0-9.]\+e-.*:\)/p' |
   tr '%pre%' '\n'
' '\n'
    
por 06.01.2017 / 18:02
1

Um pequeno script que escrevi enquanto tentava descobrir quais são os locais dos buracos em um arquivo:

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

Isto imprime coisas como:

$ echo -n 'a' >zeros; truncate -s $((4096*4)) zeros; test/report-holes.py zeros
found hole between 0x00001000 and 0x00004000 (12288 bytes)
    
por 12.02.2018 / 13:43