システムまたは特定のディレクトリツリーですべてのスパースファイルを見つける簡単な方法はありますか?
関連する場合はzsh
、Ubuntu 12.04で使用していますが、たとえばbash / shのより一般的なUnix-yの答えは問題ありません。
編集:明確にするために、私はスパースファイルを検索し、単一のスパース状態を確認しようとはしていません。
システムまたは特定のディレクトリツリーですべてのスパースファイルを見つける簡単な方法はありますか?
関連する場合はzsh
、Ubuntu 12.04で使用していますが、たとえばbash / shのより一般的なUnix-yの答えは問題ありません。
編集:明確にするために、私はスパースファイルを検索し、単一のスパース状態を確認しようとはしていません。
回答:
SEEK_HOLE
lseek
フラグをサポートしているシステム(およびファイルシステム)(ext4のUbuntu 12.04のように)で、SEEK_HOLE
Linuxの場合の値が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_HOLE
SEEK_HOLE
find . -type f ! -size 0 -printf '%S:%p\0' |
awk -v RS='\0' -F : '$1 < 1 {sub(/^[^:]*:/, ""); print}'
(この回答の以前のバージョンはfind
、たとえば3.2e-05のようにまばらであると表現された場合、適切に機能しなかったことに注意してください。私の注意を喚起してくれた@flashydaveの回答に感謝します)
find
もあからさま0バイト・ファイルを除外する必要がありますか?
find -printf '%S'
!:-)
tr
コマンドを置き換えますxargs -r0 rm -f
割り当てられたブロックの数がファイルサイズよりも小さい場合、ファイルは通常スパースです(ここ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
/を使用することもできますFIBMAP
がFIBMAP
、特にひどく遅いです...良い方法とは思えません。
for file in *
かfind
。単一のファイルをテストできる場合は、すべてのファイルをテストできますが、この方法でディレクトリを除外する必要があります。
上記の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'
ファイルの穴の位置を調べるために書きました短いスクリプト:
#!/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)