有没有一种简单的方法可以找到我的系统上或特定目录树中的所有稀疏文件?
如果相关的话,我zsh
在 Ubuntu 12.04 上使用,尽管 bash/sh 的更通用的 Unix-y 答案就可以了。
编辑:澄清一下,我正在寻找稀疏文件,而不是检查单个文件的稀疏状态。
答案1
在支持该SEEK_HOLE
lseek
标志的系统(和文件系统)上(例如 ext4 上的 Ubuntu 12.04)并假设其值为SEEK_HOLE
4,就像在 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
该 shell 语法是 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
报告稀疏性一个文件的。它采用与霜舒兹的回答因为它采用磁盘使用率与文件大小的比率,因此不能保证报告所有稀疏文件(例如,当在文件系统级别进行压缩时,或者孔节省的空间不能补偿文件系统基础设施开销或大型扩展文件时)属性),但可以在没有实现的系统或未实现的SEEK_HOLE
文件系统上工作。SEEK_HOLE
这里有 GNU 工具:
LC_ALL=C find . -type f ! -size 0 -printf '%S:%p\0' |
LC_ALL=C awk -v RS='\0' -F : '$1 < 1 {sub(/^[^:]*:/, ""); print}'
(请注意,这个答案的早期版本在表达稀疏性时无法正常工作find
,例如 3.2e-05。感谢@flashydave 的回答引起我的注意。LC_ALL=C
需要使用十进制基数来.
代替区域设置的基数(并非所有awk
实现都遵循区域设置的设置)
答案2
当分配的块数小于文件大小时,文件通常是稀疏的(这里使用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' {} +
您通常会将其放入 shell 脚本中,然后执行 shell 脚本。
find . -type f ! -size 0 -exec ./sparsetest.sh {} +
答案3
您可以找到%S
格式如下的稀疏文件find
:
# find / -type f -printf "%S\t%p\n" | gawk '$1 < 1.0 {print}'
0.0139994 /var/log/lastlog
0.959592 /usr/lib/locale/locale-archive
...
在这篇文章中找到了: https://www.thegeekdiary.com/how-to-find-all-the-sparse-file-in-linux/
答案4
我在尝试找出文件中漏洞的位置时编写的一个简短脚本:
#!/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)