【问题标题】:Summing total file sizes of directory is different by a large margin: Ruby -e, du -ach, ls -al "total"总和目录的总文件大小相差很大:Ruby -e,du -ach,ls -al“total”
【发布时间】:2021-03-17 15:37:43
【问题描述】:
ls | ruby -ne 'BEGIN{a= []}; a <<  File.size($_.chomp).to_i; END{puts a.sum}'

上面的代码获取每个文件的文件大小,将其放入一个数组中,并打印总和。

返回的值与:

du -ach

而且这两个值都与 Total 显示的有很大不同:

ls -al

没有隐藏文件。

MacOS

【问题讨论】:

  • 是不是显示了很多 4K 文件?
  • @Schwern 是的,du -ach 喜欢数字 4.0K。主要来自一个 janky rust 程序。
  • 如何定义“每个文件的大小”?它是磁盘上的大小吗?内容的大小?稀疏文件呢?文件系统压缩?重复数据删除?尾巴共享?你如何定义“总和”?如果同一个文件在一个目录中出现多次,是计算多次还是只计算一次?

标签: ruby terminal filesize ls du


【解决方案1】:

如果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 -lFile.size 报告两个字节的内容。

du,默认情况下会报告文件的块大小。这是因为它是一个磁盘使用工具,您想知道实际占用的磁盘数量。这 2 个字节占用 4K 磁盘。 1000 个 2 字节文件将占用 4000K,而不是 2000 字节。

因此,许多程序会避免使用许多小文件,而是通过将它们打包成一个 image file 来节省磁盘空间。一个简单的例子是Git packfiles

【讨论】:

  • 只有 Perl 编码人员会知道这一点。谢谢。
  • @seamus 不管你在上面堆了多少东西,熟悉堆栈的底部总是很有价值的。
【解决方案2】:

问题是您如何定义“大小”,如何定义“总和”,您是否 100% 确定您展示的所有三个示例实际上都在测量相同的东西(即所有三个都定义了这两个术语完全相同)?

这里只是一些需要考虑的例子。

稀疏文件

稀疏文件是许多文件系统的一项功能,可优化包含长时间运行的二进制零的文件的存储。该文件实际上并没有存储零,而是简单地包含文件中存在“漏洞”的信息,并且在读取文件时,操作系统将返回零,即使它们没有物理存储在文件中。

最极端的例子是一个由零组成的文件。我可以在几个字节中存储“此文件包含 2 TB 的零”信息,但是,当我要求操作系统打开并读取文件时,我会“看到”2 TB 的零。现在,这个文件的“大小”是多少?是 2TB 还是只是编码稀疏文件“洞”的信息实际需要的几个字节(在这种情况下覆盖整个文件)?

我过去常常在 1.44MB 软盘(或者最近是 32GB USB 记忆棒)上创建 TB 大小的稀疏文件来迷惑我的朋友。

元数据开销

文件系统不仅要存储文件的内容,还要存储关于文件的某种元数据:文件是什么时候创建的,文件最后一次修改的时间,文件是什么时候最后访问,谁拥有该文件,等等。

此元数据也占用空间。你算不算?请注意,每个文件系统都不同!

块大小

许多文件系统都有一个可能的最小分配大小,称为“块”。分配小于块的空间是不可能的,所以除非文件大小是块大小的整数倍,否则文件内容的大小和磁盘上文件的大小总是不同的.

这对于非常小的文件和非常大的块大小尤其明显。例如。仅包含以 ASCII 编码的字符串“Hello”的文件最多包含 7 个字节(最坏情况假设它以换行符结尾,并且换行符是 Windows 样式的 CRLF),但它将占用整个块(通常为 4KB)在磁盘上。

元数据内联

另一方面,在某些文件系统上,非常小的文件会被内联到它们的元数据条目中。因此,它们根本不需要任何 data 块。这是否意味着它们的大小为 0?

尾部共享

在某些文件系统上,多个文件的“尾部”可以共享一个块。因此,如果您有多个文件的大小不是块大小的整数倍,而不是为每个文件的每个“尾端”分配一个大部分为空的块,而是将多个文件的“尾端”填充到一个块中.

但是,现在这个block属于多个文件,所以如果你单独询问每个文件的大小,这个block会被多次报告。

同一文件的多个条目

许多文件系统将“文件”的概念与“文件名”的概念分开。例如,在 Unix 以及从它衍生或启发的任何系统(Linux、macOS、Android ……)中,“文件”只是一个未命名的数据块。 目录是一种特殊的文件,它将名称文件联系起来。

但是,这意味着一个文件可以有多个名称!那么,如果你的目录中有两个不同名字的同一个文件,那么你算一次还是两次呢?

目录条目内联

类似于元数据内联,如果文件很小,而且文件只有一个名字,那么我们可以把文件的数据放到目录中,而不是在目录项中放置一个指向文件的指针直接进入。

同样,如果我们在查看文件大小时忽略目录条目,则该文件在磁盘上的大小似乎为 0。

重复数据删除

一些文件系统执行重复数据删除,它们尝试查找具有相同内容的块,然后用指向单个块的链接透明地替换这两个块。

现在,当两个完全不相关的文件碰巧在其中的某个地方有一系列相同的内容,因此共享一些去重块时,您是计算这些块一次还是两次?

压缩

一些文件系统透明地压缩文件的内容。这意味着磁盘上文件的实际大小取决于文件内容的可压缩程度。

那么,您计算压缩后的大小还是未压缩的大小?

备用数据流/分叉

一些文件系统具有允许您在单个文件中存储多个数据流的功能。例如,NTFS 允许您在文件中存储所谓的“备用数据流”。应用程序使用它来存储其他特定于应用程序的元数据,例如音乐播放器使用它来存储音乐文件中的专辑封面,或计算歌曲的播放频率,或特定于歌曲的均衡器设置等,办公应用程序使用它来存储文件旧版本的备份,等等。 MacOS 有一个类似的功能,称为“分叉”。

几乎所有标准文件系统 API 都只会提供默认流/数据分叉。除非您使用通常特定于操作系统或特定于文件系统的 API 明确要求备用数据流/资源分叉,否则您甚至永远不会知道它的存在,但它可能非常大。

“捆绑包”

特别是在 macOS 上,您有“捆绑包”的概念,就文件系统和较低级别的操作系统而言,它在技术上是目录,但在呈现给较高级别的操作系统和操作系统时大多被视为单个文件给用户。

所以,这里有一个看起来像文件的东西,你认为“这个文件的大小应该很容易确定”,但它实际上是一个目录,包含你在问题中注意到的所有问题。

以上任意组合

当然,以上所有内容都可以相互结合。

因此,如您所见,当您计算多个文件大小的总和时,这并不是一件简单的事情。文件可以共享数据。

但即使你忘记了总和,只问单个文件的大小,答案仍然不清楚,因为有许多不同的方法来定义“大小”的含义。

所以,为了对问题有一个有意义的答案,你实际上需要退后几步,问问自己:

  1. 为什么您要测量目录文件大小的总和?你需要这些信息做什么?你的最终目标是什么?您实际上将根据这些信息做出哪些决定?您将如何使用这些信息?

  2. 您实际上需要衡量什么以获得必要的信息来作为决策依据?

  3. 如何你是如何衡量这个的?根据您对问题 #2 的回答,您需要的信息可能非常特定于操作系统或文件系统,并且是您甚至无法作为用户访问的内部文件系统 API 的一部分。

【讨论】:

    猜你喜欢
    • 2010-11-04
    • 2021-02-11
    • 1970-01-01
    • 2012-04-05
    • 2012-03-08
    • 2013-09-29
    • 2018-10-22
    • 2011-01-29
    • 1970-01-01
    相关资源
    最近更新 更多