目录的总文件大小相差很大:Ruby -e, du -ach, ls -al "total"
Summing total file sizes of directory is different by a large margin: Ruby -e, du -ach, ls -al "total"
ls | ruby -ne 'BEGIN{a= []}; a << File.size($_.chomp).to_i; END{puts a.sum}'
上面的代码获取每个文件的文件大小,将其放入数组中,并打印总和。
返回的值非常不同于:
du -ach
这两个值都与显示的总计有很大不同:
ls -al
没有隐藏文件。
MacOs
如果 du
向您展示了很多 4K 和 8K 文件,这是因为它向您展示了 block size。为了性能,磁盘上的存储由块组成。现在一个典型的块是 4K。即使是一个字节也会占用一个完整的块。
$ echo '1' > this
$ hexdump this
0000000 31 0a
0000002
$ ls -l this
-rw-r--r-- 1 schwern staff 2 Dec 5 15:16 this
$ du -h this
4.0K this
$ du --apparent-size -h this
2 this
$ ruby -e 'puts File.size(ARGV[0])' this
2
相关文件有 2 个字节的内容。 ls -l
和 File.size
报告两个字节的内容。
du
,默认情况下,报告文件的块大小。这是因为它是一个磁盘使用情况工具,而您想知道实际占用的磁盘数量。这 2 个字节占用 4K 磁盘空间。 1000 个 2 字节文件将占用 4000K,而不是 2000 字节。
出于这个原因,许多程序会避免拥有许多小文件,而是通过将它们打包成单个 image file. A simple example is Git packfiles.
来节省磁盘空间 space
问题是你如何定义“大小”,你如何定义“总和”,你是否 100% 确定你展示的所有三个例子实际上测量的是同一件事(即所有三个都定义这两个术语完全相同)?
这里只是一些需要考虑的例子。
稀疏文件
稀疏文件 是许多文件系统的一个特性,它优化了包含长 运行 二进制零的文件的存储。该文件实际上并没有 存储 零,而是仅包含文件中存在“漏洞”的信息,并且在读取文件时,OS 将 return 零,即使它们没有物理存储在文件中。
最极端的示例是包含 仅 个零的文件。我可以在几个字节中存储信息“此文件包含 2 TB 的零”,但是,当我要求操作系统打开并读取文件时,我将“看到”2 TB 的零。现在,这个文件的“大小”是多少?它是 2TB 还是实际上只需要几个字节来编码稀疏文件的“漏洞”信息(在本例中覆盖整个文件)?
我曾经通过在 1.44MB 软盘(或最近的 32GB U 盘)上创建 TB 大小的稀疏文件来迷惑我的朋友。
元数据开销
文件系统不仅要存储文件的内容,还要存储某种元数据关于文件:文件是什么时候创建的,文件最后一次修改是什么时候、上次访问文件的时间、文件的所有者等。
此元数据也占用 space。你算不算?请注意,每个文件系统都不同!
块大小
许多文件系统都有一个可能的最小分配大小,称为“块”。分配 space 小于块是不可能的,所以除非文件大小是块大小、文件内容大小和磁盘文件大小的整数倍永远不同。
这对于非常小的文件和非常大的块大小尤其明显。例如。仅包含以 ASCII 编码的字符串“Hello”的文件最多包含 7 个字节(最坏情况假设它以换行符结尾,并且换行符是 Windows 样式的 CRLF),但它会占用磁盘上的整个块(通常为 4KB)。
元数据内联
另一方面,在某些文件系统上,非常小的文件会内联到它们的元数据条目中。因此,它们根本不需要任何 data 块。这是否意味着它们的大小为 0?
尾部共享
在某些文件系统上,多个文件的“尾部”可以共享一个块。因此,如果您有多个文件,其大小不是块大小的整数倍,则不是为每个文件的每个“尾端”分配一个大部分为空的块,而是将多个文件的“尾端”填充到一个块中.
但是,现在这个块属于多个文件,所以如果你孤立地询问每个文件的大小,这个块会被报告多次。
同一文件的多个条目
许多文件系统将“文件”的概念与“文件名”的概念分开。例如,在 Unix 以及任何派生或受其启发的系统中(Linux、macOS、Android、...),“文件”只是一个未命名的数据块。 目录 是一种特殊类型的文件,它将 names 与 files.
相关联
但是,这意味着一个文件可以有多个名称!那么,如果你的目录中有同一个文件,但有两个不同的名字,那么你算一次还是两次?
目录条目内联
类似于元数据内联,如果文件很小,而且文件只有一个名字,那么我们可以把文件的数据放到目录项中,而不是在目录项中放一个指向文件的指针直接进入目录。
同样,如果我们在查看文件大小时忽略目录条目,则该文件在磁盘上的大小似乎为 0。
去重
一些文件系统执行重复数据删除,它们试图找到具有相同内容的块,然后透明地将这两个块替换为 link 到一个块。
现在,当两个完全不相关的文件碰巧在其中某处有 运行 相同的内容,因此共享一些已删除重复的块时,您是计算这些块一次还是两次?
压缩
一些文件系统透明地压缩文件的内容。这意味着磁盘上文件的实际大小取决于文件内容的可压缩性。
那么,你算压缩后的大小还是未压缩的大小?
备用数据流/分叉
一些文件系统具有允许您在单个文件中存储多个数据流的功能。例如,NTFS 允许您在文件中存储所谓的“备用数据流”。应用程序使用它来存储额外的特定于应用程序的元数据,例如音乐播放器用它来存储音乐文件中的专辑封面,或计算歌曲播放的频率,或特定于歌曲的均衡器设置等,办公应用程序用它来存储文件旧版本的备份,等等。 MacOS 有一个类似的功能叫做“Forks”。
几乎所有标准文件系统 API 都将只提供默认流/数据分叉。除非您使用通常 OS 特定或文件系统特定的 API 明确要求备用数据流/资源叉,否则您永远不会知道它在那里,但它可能非常大。
“捆绑包”
特别是在 macOS 上,您有“捆绑包”的概念,就文件系统和 OS 的较低级别而言,它在技术上是目录,但主要被视为呈现给更高级别的 OS 和用户时的单个文件。
所以,这里你有一个看起来像文件的东西,你认为“它的大小应该很容易确定”,但它实际上是一个目录,包含你在你的文件中注意到的所有问题问题。
以上任意组合
当然,以上所有内容都可以相互组合。
因此,如您所见,当您计算多个文件的大小总和时,这并不是一件简单的事情。文件可以共享数据。
但是即使你忘了总和,只问单个文件的大小,答案仍然不清楚,因为有很多不同的方式来定义什么“大小”是指。
因此,为了对问题有一个有意义的答案,您需要实际退后几步,问问自己:
为什么要测量目录文件大小的总和?你需要这些信息做什么?你的最终目标是什么?您实际上将根据这些信息做出哪些决定?您将如何使用这些信息?
什么您实际上需要进行衡量以获得决策所依据的必要信息?
你是如何测量这个的?根据您对问题 #2 的回答,您需要的信息可能非常 OS 特定或文件系统特定,以及您甚至无法作为用户访问的内部文件系统 API 的一部分。
ls | ruby -ne 'BEGIN{a= []}; a << File.size($_.chomp).to_i; END{puts a.sum}'
上面的代码获取每个文件的文件大小,将其放入数组中,并打印总和。
返回的值非常不同于:
du -ach
这两个值都与显示的总计有很大不同:
ls -al
没有隐藏文件。
MacOs
如果 du
向您展示了很多 4K 和 8K 文件,这是因为它向您展示了 block size。为了性能,磁盘上的存储由块组成。现在一个典型的块是 4K。即使是一个字节也会占用一个完整的块。
$ echo '1' > this
$ hexdump this
0000000 31 0a
0000002
$ ls -l this
-rw-r--r-- 1 schwern staff 2 Dec 5 15:16 this
$ du -h this
4.0K this
$ du --apparent-size -h this
2 this
$ ruby -e 'puts File.size(ARGV[0])' this
2
相关文件有 2 个字节的内容。 ls -l
和 File.size
报告两个字节的内容。
du
,默认情况下,报告文件的块大小。这是因为它是一个磁盘使用情况工具,而您想知道实际占用的磁盘数量。这 2 个字节占用 4K 磁盘空间。 1000 个 2 字节文件将占用 4000K,而不是 2000 字节。
出于这个原因,许多程序会避免拥有许多小文件,而是通过将它们打包成单个 image file. A simple example is Git packfiles.
来节省磁盘空间 space问题是你如何定义“大小”,你如何定义“总和”,你是否 100% 确定你展示的所有三个例子实际上测量的是同一件事(即所有三个都定义这两个术语完全相同)?
这里只是一些需要考虑的例子。
稀疏文件
稀疏文件 是许多文件系统的一个特性,它优化了包含长 运行 二进制零的文件的存储。该文件实际上并没有 存储 零,而是仅包含文件中存在“漏洞”的信息,并且在读取文件时,OS 将 return 零,即使它们没有物理存储在文件中。
最极端的示例是包含 仅 个零的文件。我可以在几个字节中存储信息“此文件包含 2 TB 的零”,但是,当我要求操作系统打开并读取文件时,我将“看到”2 TB 的零。现在,这个文件的“大小”是多少?它是 2TB 还是实际上只需要几个字节来编码稀疏文件的“漏洞”信息(在本例中覆盖整个文件)?
我曾经通过在 1.44MB 软盘(或最近的 32GB U 盘)上创建 TB 大小的稀疏文件来迷惑我的朋友。
元数据开销
文件系统不仅要存储文件的内容,还要存储某种元数据关于文件:文件是什么时候创建的,文件最后一次修改是什么时候、上次访问文件的时间、文件的所有者等。
此元数据也占用 space。你算不算?请注意,每个文件系统都不同!
块大小
许多文件系统都有一个可能的最小分配大小,称为“块”。分配 space 小于块是不可能的,所以除非文件大小是块大小、文件内容大小和磁盘文件大小的整数倍永远不同。
这对于非常小的文件和非常大的块大小尤其明显。例如。仅包含以 ASCII 编码的字符串“Hello”的文件最多包含 7 个字节(最坏情况假设它以换行符结尾,并且换行符是 Windows 样式的 CRLF),但它会占用磁盘上的整个块(通常为 4KB)。
元数据内联
另一方面,在某些文件系统上,非常小的文件会内联到它们的元数据条目中。因此,它们根本不需要任何 data 块。这是否意味着它们的大小为 0?
尾部共享
在某些文件系统上,多个文件的“尾部”可以共享一个块。因此,如果您有多个文件,其大小不是块大小的整数倍,则不是为每个文件的每个“尾端”分配一个大部分为空的块,而是将多个文件的“尾端”填充到一个块中.
但是,现在这个块属于多个文件,所以如果你孤立地询问每个文件的大小,这个块会被报告多次。
同一文件的多个条目
许多文件系统将“文件”的概念与“文件名”的概念分开。例如,在 Unix 以及任何派生或受其启发的系统中(Linux、macOS、Android、...),“文件”只是一个未命名的数据块。 目录 是一种特殊类型的文件,它将 names 与 files.
相关联但是,这意味着一个文件可以有多个名称!那么,如果你的目录中有同一个文件,但有两个不同的名字,那么你算一次还是两次?
目录条目内联
类似于元数据内联,如果文件很小,而且文件只有一个名字,那么我们可以把文件的数据放到目录项中,而不是在目录项中放一个指向文件的指针直接进入目录。
同样,如果我们在查看文件大小时忽略目录条目,则该文件在磁盘上的大小似乎为 0。
去重
一些文件系统执行重复数据删除,它们试图找到具有相同内容的块,然后透明地将这两个块替换为 link 到一个块。
现在,当两个完全不相关的文件碰巧在其中某处有 运行 相同的内容,因此共享一些已删除重复的块时,您是计算这些块一次还是两次?
压缩
一些文件系统透明地压缩文件的内容。这意味着磁盘上文件的实际大小取决于文件内容的可压缩性。
那么,你算压缩后的大小还是未压缩的大小?
备用数据流/分叉
一些文件系统具有允许您在单个文件中存储多个数据流的功能。例如,NTFS 允许您在文件中存储所谓的“备用数据流”。应用程序使用它来存储额外的特定于应用程序的元数据,例如音乐播放器用它来存储音乐文件中的专辑封面,或计算歌曲播放的频率,或特定于歌曲的均衡器设置等,办公应用程序用它来存储文件旧版本的备份,等等。 MacOS 有一个类似的功能叫做“Forks”。
几乎所有标准文件系统 API 都将只提供默认流/数据分叉。除非您使用通常 OS 特定或文件系统特定的 API 明确要求备用数据流/资源叉,否则您永远不会知道它在那里,但它可能非常大。
“捆绑包”
特别是在 macOS 上,您有“捆绑包”的概念,就文件系统和 OS 的较低级别而言,它在技术上是目录,但主要被视为呈现给更高级别的 OS 和用户时的单个文件。
所以,这里你有一个看起来像文件的东西,你认为“它的大小应该很容易确定”,但它实际上是一个目录,包含你在你的文件中注意到的所有问题问题。
以上任意组合
当然,以上所有内容都可以相互组合。
因此,如您所见,当您计算多个文件的大小总和时,这并不是一件简单的事情。文件可以共享数据。
但是即使你忘了总和,只问单个文件的大小,答案仍然不清楚,因为有很多不同的方式来定义什么“大小”是指。
因此,为了对问题有一个有意义的答案,您需要实际退后几步,问问自己:
为什么要测量目录文件大小的总和?你需要这些信息做什么?你的最终目标是什么?您实际上将根据这些信息做出哪些决定?您将如何使用这些信息?
什么您实际上需要进行衡量以获得决策所依据的必要信息?
你是如何测量这个的?根据您对问题 #2 的回答,您需要的信息可能非常 OS 特定或文件系统特定,以及您甚至无法作为用户访问的内部文件系统 API 的一部分。