スパースファイルを見つけますか?


19

システムまたは特定のディレクトリツリーですべてのスパースファイルを見つける簡単な方法はありますか?

関連する場合はzsh、Ubuntu 12.04で使用していますが、たとえばbash / shのより一般的なUnix-yの答えは問題ありません。

編集:明確にするために、私はスパースファイルを検索し、単一のスパース状態を確認しようとはしていません。



2
スパースファイルの検索に、個々のファイルのスパース状態の確認が含まれないのはなぜですか?
jlliagre

回答:


11

SEEK_HOLE lseekフラグをサポートしているシステム(およびファイルシステム)(ext4のUbuntu 12.04のように)で、SEEK_HOLELinuxの場合の値が4であると想定している場合:

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の答えと同じアプローチを取ります。ディスク使用量とファイルサイズの比率を取るため、すべてのスパースファイルを報告することは保証されません(ファイルシステムレベルで圧縮がある場合や、穴によって節約されるスペースがファイルシステムインフラストラクチャのオーバーヘッドまたは大きな拡張属性を補います)が、実装されていないシステムまたは実装されていないファイルシステムで動作します。GNUツールの場合:SEEK_HOLESEEK_HOLE

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
ステファンChazelas

8

割り当てられたブロックの数がファイルサイズよりも小さい場合、ファイルは通常スパースです(ここstatではUbuntuで見つかったGNU を使用しますが、他のシステムではの互換性のない実装があることに注意してくださいstat)。

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

バリアントfind:(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' {} +

通常、代わりにシェルスクリプトにこれを入れてから、シェルスクリプトを実行します。

find . -type f ! -size 0 -exec ./sparsetest.sh {} +

たとえば、スパースブロックではなく、従来のファイルシステムの間接ブロックのオーバーヘッドをカバーするのに十分でない場合、スパースではなく圧縮によって割り当てられたスペースの量が減少する場合は、機能しない可能性があります。
ステファンシャゼル

承知しました; SEEK_HOLEただし、多くのプラットフォーム/ファイルシステムでサポートされていないため、同様に問題があります。LinuxではFIEMAP/を使用することもできますFIBMAPFIBMAP、特にひどく遅いです...良い方法とは思えません。
frostschutz

また、これらの方法の多くでは、最初にファイルを同期する必要があります。
frostschutz

ありがとう。しかし、それは本当に質問に答えません。特定のファイルがスパースかどうかを確認するのではなく、システム上のすべてのスパースファイルを検索します。
アンドリューフェリエ

1
申し訳ありません@AndrewFerrier、私はでこれをラップする些細な十分だと思ったと思いますfor file in *find。単一のファイルをテストできる場合は、すべてのファイルをテストできますが、この方法でディレクトリを除外する必要があります。
frostschutz

3

上記のStephane Chazelasの答えは、%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'

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)

スパースファイルを探していたので、私の質問には答えません。特定のファイルの穴ではなく、有用で関連性のあるスクリプトです。ありがとう。賛成。
アンドリューフェリエ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.