🐧

duとwcの違い

2021/03/22に公開

duとwc

ファイルサイズを知りたい時に使うコマンドとして、du と wc がある。これらの違いは何だろう?

Ubuntu 18.04 LTS で GNU Coreutils の du と wc で確認する。

duは4096バイト単位に切り上げる...とは限らない

du は通常4096バイト単位に切り上げたサイズを返す。

$ echo -n 12345 > xxx
$ du xxx
4	xxx
$ du --block-size=1 xxx
4096	xxx

実装を確認すると、fstatat(2) で得られた stat 構造体から stat.st_blocks * 512 の値を返している。上の例だと st_blocks が8の倍数になっている。

coreutils/src/du.c

duinfo_set (&dui,
            (apparent_size
             ? MAX (0, sb->st_size)
             : (uintmax_t) ST_NBLOCKS (*sb) * ST_NBLOCKSIZE), /* HERE */
            (time_type == time_mtime ? get_stat_mtime (sb)
             : time_type == time_atime ? get_stat_atime (sb)
             : get_stat_ctime (sb)));

gnulib/lib/stat-size.h

# define ST_NBLOCKS(statbuf) ((statbuf).st_blocks)
/* ... */
#  define ST_NBLOCKSIZE 512

一方で、-b オプションをつけると、4096バイト単位に切り上げたサイズではなく、実際のファイルの中身のサイズが返ってくる。

$ echo -n 12345 > xxx
$ du -b xxx
5	xxx

したがって、duの出力が常に4096バイト単位に切り上げられるわけではない。

実装を確認すると、この場合には stat.st_size の値を返している。

coreutils/src/du.c

duinfo_set (&dui,
            (apparent_size
             ? MAX (0, sb->st_size) /* HERE */
             : (uintmax_t) ST_NBLOCKS (*sb) * ST_NBLOCKSIZE),
            (time_type == time_mtime ? get_stat_mtime (sb)
             : time_type == time_atime ? get_stat_atime (sb)
             : get_stat_ctime (sb)));

wcはreadする...とは限らない

wcをオプションなしで実行すると、対象のファイルをreadし、lines、words、bytes を出力する。

$ echo -n 12345 > xxx
$ wc xxx
0 1 5 xxx
$ strace wc xxx 2>&1 >/dev/null
...
openat(AT_FDCWD, "xxx", O_RDONLY)       = 3
fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
read(3, "12345", 16384)                 = 5
read(3, "", 16384)                      = 0
...

words, linesを取得するためにはファイルの内容を読み込む必要があるため。

https://github.com/coreutils/coreutils/blob/86c8dc2e85c0941ec7908cd5a3db7803e7293606/src/wc.c#L380

一方、wc -c で bytes のみを取得する場合はreadせずにサイズを返すという動きをする。

$ wc -c xxx
5 xxx
$ strace wc -c xxx 2>&1 >/dev/null
...
openat(AT_FDCWD, "xxx", O_RDONLY)       = 3
fstat(3, {st_mode=S_IFREG|0664, st_size=5, ...}) = 0
...

fstat(2) で stat 構造体を取得し、stat.st_size の値を返している。

https://github.com/coreutils/coreutils/blob/86c8dc2e85c0941ec7908cd5a3db7803e7293606/src/wc.c#L285

duはファイルをopenしない

wc はファイルをオープンしてそのサイズを取得するが、du はファイルをオープンせずに fstatat(2) でサイズを取得する。

したがって、du だと読み取りアクセス許可が無いファイルのサイズも取得できるが、wc だとエラー(Permission denied)になる。

$ echo -n 12345 > xxx
$ chmod -r xxx
$ ls -l xxx
--w--w---- 1 vagrant vagrant 5 Mar 21 22:13 xxx
$ du -b xxx
5	xxx
$ wc -c xxx
wc: xxx: Permission denied

まとめ

  • duは4096バイト単位に切り上げたサイズを返すこともあるが、そうじゃ無い場合もある。
  • wcはファイルをreadすることもあるが、しないこともある。
  • du はファイルをopenしない。wcはファイルをopenする。

Discussion