Notes

du * 输出的其实不是我们通常理解上的文件大小,而是所谓 block size(block count 可能更准确)。而且,它显示的是 disk usage,也就是这个文件占了多大的硬盘容量,或者说文件系统实际分配了多少空间给到这个文件。而不是,这个文件,它本身有多大。这个区别比较关键,后面会有更多的说明。

$ du *
5568	1-2-1_2frames_ap4h.mov
8040	1-2-1_2frames_ap4x.mov
3928	1-2-1_2frames_apch.mov
2152	1-2-1_2frames_apcn.mov
688		1-2-1_2frames_apco.mov
1592	1-2-1_2frames_apcs.mov

我们使用 exa 这个 ls 的替代,用 exa -lh -b -S 这个命令得到这样的输出:

$ exa -lh -b -S
Permissions  Size Blocks User Date Modified Name
.rw-r--r--@ 2.7Mi   5568 thom 27 Oct 17:32  1-2-1_2frames_ap4h.mov
.rw-r--r--@ 3.9Mi   8040 thom 27 Oct 17:32  1-2-1_2frames_ap4x.mov
.rw-r--r--@ 1.9Mi   3928 thom 27 Oct 17:32  1-2-1_2frames_apch.mov
.rw-r--r--@ 1.0Mi   2152 thom 27 Oct 17:32  1-2-1_2frames_apcn.mov
.rw-r--r--@ 342Ki    688 thom 27 Oct 17:32  1-2-1_2frames_apco.mov
.rw-r--r--@ 793Ki   1592 thom 27 Oct 17:32  1-2-1_2frames_apcs.mov
  • -l: Display extended file metadata as a table.
  • -h: Add a header row to each column.
  • -b: List file sizes with binary prefixes. --binary.
  • -S: List each file’s number of file system blocks.

可以看到 Blocks 那一栏跟 du * 的输出是一样的。-S 的 flag 的作用也正是列出 file system blocks。


使用 du -k * 会得到以下的输出:

$ du -k *
2784	1-2-1_2frames_ap4h.mov
4020	1-2-1_2frames_ap4x.mov
1964	1-2-1_2frames_apch.mov
1076	1-2-1_2frames_apcn.mov
344	    1-2-1_2frames_apco.mov
796	    1-2-1_2frames_apcs.mov
  • -k: Display block counts in 1024-byte (1 KiB) blocks.

可以看到这回输出的数字又跟上面的不一样了。我们 pass -k 这个 flag 给到 du,是让 du 把 1024 个 byte 当作是一个 block,然后告诉我们这个文件一共有多少个 1024 bytes 的 block。

我们可以把输出的这一串数字当作是 KiB,因为 1024 bytes = 1KiB。要得到 bytes 的话,把它们分别乘以 1024 就可以了,比如第一个文件 1-2-1_2frames_ap4h.mov 显示为 2784:2784 * 1024 bytes = 2850816 bytes。

但使用 Finder 查看第一个文件会发现,字节数跟我们计算出来的不太一样,Finder 显示的是:2849756。2850816 - 2849756 = 1060,相差了 1060 个 bytes。这是为什么呢?

上面的 du * 命令,不 pass 任何 flag 的,则是把 512 个 byte 当作一个 block,告诉我们这个文件有多少个 512 bytes 的 block。这是 macOS 上 du 命令的默认值,默认的 block size。而在 Linux 上,或者你在 macOS 上使用 GNU du(gdu),默认值则是 1024 bytes。


exa -lh -B -S 得到与 Finder 匹配的字节数 bytes,其中 -B 的意思是 List file sizes in bytes, without any prefixes.

exa -lh -b -S 得到二进制下的 MiB:2.8Mi

exa -lh -S 不 pass -b-B 得到与 Finder 匹配的 MB:2.8M。但有可能由于四舍五入位数的不同而导致比如 Finder 显示 64.6MB,这里显示 65M。

  • exa -lh -B -S: bytes
  • exa -lh -b -S: binary prefix
  • exa -l: exa default to decimal prefix with this command.

du 输出的是 block size。什么是 block size 呢,就是文件系统 file system 实际分配给这个文件的空间。而其他命令比如说 exa -lh -b -S 输出的结果是所谓 apparent size(表面上的大小),就是这个文件看上去应该是这么大(看上去应该是占据了这么多的空间)。也就是说,一个文件的 metadata 里显示的我这个文件的大小(表面上的大小,apparent size),“我这个文件是这么大哦”,与实际上文件系统分配给(allocate)给它的空间,“我只分配给你这么多空间,因为你只需要这么多空间”,可以是不同的,是两码事。

为什么呢?因为有一种叫做 sparse file 的存在。就是你说你要占据这么多的空间,但实际上你拿这么多空间来存储的都是 0,你没往里面写入数据。使用 dd if=/dev/null of=big bs=1 count=0 seek=2G 命令可以生成一个 apparent size 是 2 GiB,实际上文件系统分配给它的空间是 0 的文件。因为这个 command 实际上没有写入任何数据到那个文件当中去。或者你可以生成一个文件,先写入 1 MiB 的数据到文件的最开头,再写入 1 MiB 的数据到文件的最末尾,然后中间什么都不写入,是空的。那么文件系统会把这个文件记做只占据 2 MiB 空间,只分配 2 MiB 给它。然后它实际上也只占据了 2 MiB。


macOS 自带的 du 命令的行为实在是太奇怪了,要么 flags 跟 GNU 的 du 不一样,非得自己改一下,改个别的名,比如 GNU du 要显示 apparent size 的话,pass --apparent-size flag:du --apparent-size --block-size 1 *;macOS du 要显示 apparent size 的话,pass -A flag。但关键的来了,这个 -A 它不 work!

对于 macOS du 来说,du -A -B 1 * 对应着 GNU du 的 du --apparent-size --block-size 1 *。是吧,就功能上来说,-A 对应 --apparent-size-B 对应 --block-size。这边 GNU du 的 du --apparent-size --block-size 1 * 输出结果为:

64606136	A100_001_20231023.mov
118636627	A100_002_20231023.mov
99361921	A100_003_20231023.mov

但 macOS du 的 du -A -B 1 * 输出结果为:

126184	A100_001_20231023.mov
231713	A100_002_20231023.mov
194067	A100_003_20231023.mov

它输出的仍然是 disk usage 而不是 apparent size,而且输出的这个数字还是 block size 为 512 bytes 下的 block count,而不是我 pass 给它的 1,要求它输出 block size 为 1 bytes 下的 block count——也就是所有的 bytes(bytes 就是它二者相乘得到的:block size * block count = total bytes)。相当于这两个 flag 都不 work。

认为 GNU du 输出的那三个数字是对的的原因,是与其他命令交叉比对得到的结果:diskus -b A100_002_20231023.mov, exa -lh -B -S

所以咱直接不用 macOS 自带的 du 了,可以 brew install coreutils,里面就有纯正的原生的 du,告别 macOS 魔改过的 du。


最后,记住这几个 command 就可以了:

  • du --si *: --si, like -h (human readable), but use powers of 1000 not 1024.
  • du -b *: -b equivalent to --apparent-size --block-size=1.
  • exa -lh -B -S:
    • -l: Display extended file metadata as a table.
    • -h: Add a header row to each column.
    • -B: List file sizes in bytes, without any prefixes.
    • -S: List each file’s number of file system blocks. Use block size 512 bytes actually.
  • diskus:
    • diskus -b --size-format binary <file>
    • diskus -b --size-format decimal <file>

查看所在的盘的文件系统的 block size:stat -f %k .


  • gdu -sh --block-size 1 <file>: disk usage
  • gdu -sh -b <file>: apparent size

diskus 为了与 du 的行为匹配,默认使用 disk usage。要查看文件的 apparent size,pass -b flag:

$ diskus -b <file>
64.61 MB (64,606,136 bytes)

$ diskus <file>
64.61 MB (64,606,208 bytes)

gdu 默认的 block size 是 1024,du 默认是 512。所以当不 pass 任何 flag 给到它们两个命令时:

$ du A100_001_20231023.mov
126184	A100_001_20231023.mov

$ gdu A100_001_20231023.mov
63092	A100_001_20231023.mov

这里显示的数字是 block count 的意思。有这么多 block,在分别以 block size 为 512 和 1024 bytes 的情况下。du 输出的数字 126184 的意思是:这里有 126184 个 512 bytes 大小的 block。gdu 输出的数字 63092 的意思是:这里有 63092 个 1024 bytes 大小的 block。所以最终的 bytes 字节数:

  • 126184 * 512 = 64606208 bytes
  • 63092 * 1024 = 64606208 bytes

这里都是 disk usage。


Standards Prefix
IEC 60027 binary prefix
The SI prefixes decimal prefix

严格来说,标准定义的用来表示计量单位的倍数的前缀当中,没有 KB 这种写法,应该写做 kB。这是 10 进制下的。而对于 2 进制倒是有 KiB(大写 “K”),这是标准的写法。然后如果去掉后面的 “B” 的话,对于 10 进制也应该写做小写的 k 而不是 K(但是这里有一个奇葩),对于 2 进制的话则写做 Ki。其他前缀比如 mega、giga,mebi、gibi 等,也是同样的逻辑。

Reference