当然,测试文件是否为空的标准方法是使用test -s FILE
,但我们的一个客户收到了一个包含如下测试的脚本:
RETVAL=`ls -s ./log/cr_trig.log | awk '{print $1}'`
if test $RETVAL -ne 0
then
echo "Badness: Log not empty"
exit 25
fi
供应商声称它可以在他们测试的两个环境中工作。不用说,它在我测试的两个地方都严重失败。
所以,我很好奇。什么时候ls -s
打印0
空文件?
这是我迄今为止的发现:
- Linux 上的 GFS:4
- Linux 上的 ext4:0
- Solaris 上的 ZFS:1
- Solaris 上的 UFS:0
- AIX 上的 jfs:0
- HP-UX 上的 VxFS:0
- HP-UX 上的 HFS:0
- Mac OS X 上的 HFS:0
我还没有研究过网络文件系统。
问题:我怎样才能优雅地向其他人解释他们的脚本是错误的?
在我看来,“正确”的版本是:
if test ! -s ./log/cr_trig.log
then
echo "Badness: Log not empty"
exit 25
fi
答案1
非常有趣的发现。尽管我从未使用过ls -s
检查文件是否为空,但我会假设它0
也会报告空文件。
对于你的问题:如垫已经发表评论,向他们展示您的测试结果。为了向他们解释结果,请声明ls -s
报告文件系统中已分配块的数量,而不是实际大小(以字节为单位)。显然,某些文件系统实现即使不需要存储任何数据,也会分配块,而不是仅在 inode 中存储 NULL 指针。
对此的解释可能与性能有关。创建将保持为空的空文件是正常处理的一个例外(我见过的最常见用法是创建状态文件,其中文件的存在代表软件的某种状态)。
但通常创建的文件很快就会得到一些数据,所以某个文件系统的设计者可能认为在文件创建时立即分配一个数据块是值得的,所以当第一个数据到达时这个任务就已经完成了。
第二个原因可能是文件包含过去已被删除的数据。与其释放最后一个数据块,不如保留该数据块以供同一文件重用。
编辑:
我想到了另一个原因:您发现值 > 0 的文件系统是ZFS、RAID+LVM+FS的实现和政府财政司司长,一个集群文件系统。两者都可能必须存储元数据以维护未存储在 inode 中的文件完整性。可能是ls -s
为该元数据分配的数据块中的计数。
答案2
与大多数(如果不是全部)其他文件系统不同,ZFS 不会预先分配静态 inode 数组。在 ZFS 上创建一个空文件将使用一个新的数据块,该数据块是 报告的数据块ls -s
。
我怀疑 GFS 必须存储同步/锁定数据,从而导致其他非零结果。
答案3
ls -s
报告为文件分配的块数,不包括直接存储在目录条目中的任何内容。
在大多数情况下,块数等于字节数除以块大小(以字节为单位),向上舍入。
块的数量可以少于稀疏文件。例如,在大多数文件系统上,这将创建一个跨越 0 个块的 8192 字节文件:
$ perl -e 'truncate STDOUT, 8192' >a
$ ls -l a
-rw-r--r-- 1 gilles gilles 8192 Nov 1 21:32 a
$ ls -s a
0 a
相反,如果文件系统为文件预先分配块或使用块来存储元数据,则块的数量可能会更多。考虑到 Zfs 提供的大量功能及其面向大型文件系统的方向,我对 Zfs 在文件大小和块数之间存在不明显的对应关系并不感到惊讶;我不知道细节,但块的数量不仅取决于文件的大小,还取决于其历史记录(如果是截断较大文件的结果,则空文件中可以有多个块)。
解释一下为什么ls -s
是错误的:它不计算文件的大小,而是计算依赖于文件系统的数量。这是一种非常间接的方法来确定文件是否为空,需要外部工具(ls
)和一些解析;相反,他们应该使用test -s
,它不需要解析,并且完全执行所请求的操作。如果他们认为这ls -s
是测试文件是否为空的好方法,那么他们应该有责任证明它有效。