¿Encontrar archivos dispersos?

19

¿Hay una manera sencilla de encontrar todos los archivos dispersos en mi sistema o en un árbol de directorios en particular?

Si es relevante, estoy usando zshUbuntu 12.04, aunque una respuesta Unix-y más genérica para bash / sh, por ejemplo, estaría bien.

Editar : para aclarar, estoy buscando buscar archivos dispersos, no verificar el estado de dispersión de uno solo.

Andrew Ferrier
fuente
2
¿Qué te hace sentir que la búsqueda de archivos dispersos no implica verificar el estado de dispersión de los archivos individuales?
jlliagre

Respuestas:

11

En sistemas (y sistemas de archivos) que admiten el SEEK_HOLE lseekindicador (como lo haría su Ubuntu 12.04 en ext4) y suponiendo que el valor SEEK_HOLEes 4 como en 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

Esa sintaxis de shell es POSIX. Las cosas no portátiles que contiene son perly eso SEEK_HOLE.

lseek(SEEK_HOLE)busca el inicio del primer agujero en el archivo, o el final del archivo si no se encuentra ningún agujero. Arriba sabemos que el archivo no es escaso cuando lseek(SEEK_HOLE)nos lleva al final del archivo (al mismo lugar que lseek(SEEK_END)).

Si desea enumerar los archivos dispersos:

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

El GNU find(desde la versión 4.3.3) tiene -printf %Sque informar la escasez de un archivo. Toma el mismo enfoque que la respuesta de frostschutz, ya que toma la proporción de uso del disco frente al tamaño del archivo, por lo que no se garantiza que informe todos los archivos dispersos (como cuando hay compresión a nivel del sistema de archivos o donde el espacio ahorrado por los agujeros no compensar la sobrecarga de la infraestructura del sistema de archivos o los grandes atributos extendidos), pero funcionaría en sistemas que no tienen SEEK_HOLEo sistemas de archivos donde SEEK_HOLEno se implementa. Aquí con las herramientas de GNU:

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

(tenga en cuenta que una versión anterior de esta respuesta no funcionó correctamente cuando findexpresó la escasez como, por ejemplo, 3.2e-05. Gracias a la respuesta de @ flashydave por llamar mi atención)

Stéphane Chazelas
fuente
Mismo comentario que el anterior; Estoy buscando una manera de encontrar todos los archivos dispersos, no verificar un archivo en particular.
Andrew Ferrier el
1
¿Quizás findtambién debería excluir directamente los archivos de 0 bytes?
frostschutz
@frostschutz, buen punto, respuesta actualizada.
Stéphane Chazelas
Bonito hallazgo con el find -printf '%S'! :-)
frostschutz
1
@Brian, reemplaza el trcomando conxargs -r0 rm -f
Stéphane Chazelas
8

Un archivo generalmente es escaso cuando la cantidad de bloques asignados es menor que el tamaño del archivo (aquí usando GNU statcomo se encuentra en Ubuntu, pero tenga cuidado con otros sistemas que pueden tener implementaciones incompatibles stat).

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

Variante con find: (robado 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' {} +

Por lo general, debe poner esto en un script de shell, luego ejecutar el script de shell.

find . -type f ! -size 0 -exec ./sparsetest.sh {} +
Frostschutz
fuente
Eso puede no funcionar si los bloques dispersos no son suficientes para cubrir la sobrecarga de los bloques indirectos en los sistemas de archivos tradicionales, por ejemplo, si la compresión en lugar de la dispersión está reduciendo la cantidad de espacio asignado.
Stéphane Chazelas
Seguro; SEEK_HOLESin embargo, es igual de problemático, ya que no es compatible con muchas plataformas / sistemas de archivos. En Linux también podría usar FIEMAP/ FIBMAP, pero FIBMAPen particular es terriblemente lento ... simplemente no parece ser una buena manera.
frostschutz
Además, muchos de estos métodos requieren que el archivo se sincronice primero.
frostschutz
Gracias. Sin embargo, eso realmente no responde la pregunta. No estoy buscando verificar si un archivo en particular es escaso, sino encontrar todos los archivos dispersos en el sistema.
Andrew Ferrier el
1
@AndrewFerrier lo siento, creo que pensé que era lo suficientemente trivial como para envolver esto en una for file in *o find. Si puede probar un solo archivo, puede probar todos los archivos ... aunque debe excluir directorios con este método.
frostschutz
3

La respuesta anterior de Stephane Chazelas no tiene en cuenta el hecho de que algunos archivos dispersos con el parámetro find% S informan la relación como números de coma flotante como

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

Estos se pueden encontrar además de

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

Una secuencia de comandos breve que escribí al tratar de averiguar cuáles son las ubicaciones de los agujeros en un archivo:

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

Esto imprime cosas como:

$ echo -n 'a' >zeros; truncate -s $((4096*4)) zeros; test/report-holes.py zeros
found hole between 0x00001000 and 0x00004000 (12288 bytes)
zbyszek
fuente
No responde a mi pregunta ya que estaba buscando archivos dispersos, no los agujeros en un archivo específico, pero aún así un script útil / relevante. Gracias. Votado
Andrew Ferrier