Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

Is there a straightforward way to find all the sparse files on my system, or in a particular directory tree?

If it's relevant, I'm using zsh on Ubuntu 12.04, although a more generic Unix-y answer for bash/sh, for example, would be fine.

Edit: to clarify, I'm looking to search for sparse files, not check the sparseness status of a single one.

share|improve this question
1  
1  
What makes you feel searching for sparse files does not involve checking the sparseness status of individual ones ? –  jlliagre Aug 12 '13 at 22:34

2 Answers 2

up vote 3 down vote accepted

On systems with the SEEK_HOLE lseek flag (like your Ubuntu 12.04) would (and assuming the value for SEEK_HOLE is 4 as it is on 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

That shell syntax is POSIX. The non-portable stuff in it are perl and that SEEK_HOLE.

If you want to list the sparse files:

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

The GNU find has -printf %S to report the sparseness of a file. It takes the same approach as frostschutz' answer in that it takes the ratio of disk usage vs file size, so is not guaranteed to report all sparse files, but would work on systems that don't have SEEK_HOLE or file systems where SEEK_HOLE is not implemented. Here with GNU tools:

find . -type f ! -size 0 -printf '%S:%p\0' |
  sed -zn 's/^0[^:]*://p' |
  tr '\0' '\n'
share|improve this answer
    
Same comment as above; I'm looking for a way to find all sparse files, not check a particular file. –  Andrew Ferrier Aug 12 '13 at 19:12
1  
Maybe find should also exclude 0-byte-files outright? –  frostschutz Aug 12 '13 at 19:56
    
@frostschutz, good point, answer updated. –  Stéphane Chazelas Aug 12 '13 at 20:04
    
Nice find with the find -printf '%S'! :-) –  frostschutz Aug 14 '13 at 10:29

A file is usually sparse when the number of allocated blocks is smaller than the file size (here using GNU stat as found on Ubuntu, but beware other systems may have incompatible implementations of stat).

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

Variant with find: (stolen from 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' {} +

You'd usually put this in a shell script instead, then exec the shell script.

find . -type f ! -size 0 -exec ./sparsetest.sh {} +
share|improve this answer
    
That may not work if the sparse blocks are not enough to cover for the overhead of indirect blocks in traditional file systems for instance, of if compression instead of sparseness is reducing the amount of allocated space. –  Stéphane Chazelas Aug 12 '13 at 17:28
    
Sure; SEEK_HOLE is just as problematic though, as it's not supported by many platforms/filesystems. In Linux you could also use FIEMAP/FIBMAP, but FIBMAP in particular is horribly slow... there just doesn't seem to be a good way. –  frostschutz Aug 12 '13 at 17:51
    
Also a lot of these methods require the file to be synced first. –  frostschutz Aug 12 '13 at 17:57
    
Thanks. That doesn't really answer the question, though. I'm not looking to check if a particular file is sparse, but to find all sparse files on the system. –  Andrew Ferrier Aug 12 '13 at 19:11
    
What do you mean by the file needs to be synced first? –  Stéphane Chazelas Aug 12 '13 at 19:44

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.