du 命令详解与参数实测(Ubuntu 22.04)

du 命令逐参数实测详解:默认递归 KB 统计、-h 人类可读、-s 汇总、-a 全文件、-b 真实字节、--max-depth 分层、--exclude 排除、-c 总计,并实测 du 与 df 统计口径差异及 deleted-but-open 场景,全部回显在 Ubuntu 22.04(GNU coreutils 8.32)实测通过。

Linux命令Linux系统UbuntuUbuntu 22.04命令行文件管理2026/10/15

du 命令详解与参数实测(Ubuntu 22.04)

目标:全面掌握 GNU du 的每一个参数——默认递归统计(KB 单位)、-h 人类可读、-s 汇总、-a 列出所有文件、-b 真实字节、--max-depth 分层统计、--exclude 模式排除、-c 总计,并实测 du 与 df 的统计口径差异,全部命令与回显已在 Ubuntu 22.04(GNU coreutils 8.32)实测通过。

一、环境信息

  • 操作系统:Ubuntu 22.04.5 LTS(x86_64)
  • du 版本:GNU coreutils 8.32(du --version 实测)
  • du 属于 coreutils 基础工具集,Ubuntu 22.04 默认预装,无需额外安装
  • 演示目录:/root/demo/du/
  • 测试数据:logs/(app.log 3MB、error.log 2MB)、data/raw/(users.csv 5MB、orders.csv 4MB)、data/index.bin 2MB、docs/notes.txt 5 字节、README.md 7 字节,总计约 14MB

二、命令概览

2.1 语法与测试数据准备

du(disk usage)的基本语法是 du [OPTION]... [FILE]...:对每个 FILE 递归统计其占用的磁盘块数并输出,缺省时统计当前目录。注意 du 统计的是「磁盘占用」而非「文件内容大小」——文件按块对齐分配,1 字节的文件也要占一个 4KiB 数据块,这是后面多个实测结论的根源。

💡 技术拓展:"看磁盘用了多少"有一族工具,统计口径各不相同,横向对比——

工具原理/职责适用场景
du沿目录树逐文件累计磁盘占用定位"哪个目录占的空间大"
df按文件系统整体统计已用/可用块看"整个分区还剩多少空间"
stat -c %s单文件的内容字节数(非块占用)看单个文件的精确大小
ncdudu 结果的交互式浏览(需另装)交互式逐层下钻清理空间

本文主角是 du:口径是"磁盘占用"(块对齐后),与 df 的"文件系统整体"互补——du 找元凶、df 看总量。

先准备一套多层目录、多种大小的演示数据,后续所有参数实测都基于这套数据:

rm -rf /root/demo/du && mkdir -p /root/demo/du/logs /root/demo/du/data/raw /root/demo/du/docs
dd if=/dev/zero of=/root/demo/du/logs/app.log bs=1M count=3 status=none
dd if=/dev/zero of=/root/demo/du/logs/error.log bs=1M count=2 status=none
dd if=/dev/zero of=/root/demo/du/data/raw/users.csv bs=1M count=5 status=none
dd if=/dev/zero of=/root/demo/du/data/raw/orders.csv bs=1M count=4 status=none
dd if=/dev/zero of=/root/demo/du/data/index.bin bs=1M count=2 status=none
printf 'readme\n' > /root/demo/du/README.md
printf 'note\n' > /root/demo/du/docs/notes.txt

验证数据就绪:

$ ls -lhR /root/demo/du
/root/demo/du:
total 16K
-rw-r--r-- 1 root root    7 Oct 15 06:28 README.md
drwxr-xr-x 3 root root 4.0K Oct 15 06:28 data
drwxr-xr-x 2 root root 4.0K Oct 15 06:28 docs
drwxr-xr-x 2 root root 4.0K Oct 15 06:28 logs

/root/demo/du/data:
total 2.1M
-rw-r--r-- 1 root root 2.0M Oct 15 06:28 index.bin
drwxr-xr-x 2 root root 4.0K Oct 15 06:28 raw

/root/demo/du/data/raw:
total 9.0M
-rw-r--r-- 1 root root 4.0M Oct 15 06:28 orders.csv
-rw-r--r-- 1 root root 5.0M Oct 15 06:28 users.csv

/root/demo/du/docs:
total 4.0K
-rw-r--r-- 1 root root 5 Oct 15 06:28 notes.txt

/root/demo/du/logs:
total 5.0M
-rw-r--r-- 1 root root 3.0M Oct 15 06:28 app.log
-rw-r--r-- 1 root root 2.0M Oct 15 06:28 error.log

2.2 --help 全量回显

du --help 输出全部参数清单(实测原样保留,可作为参数速查):

Usage: du [OPTION]... [FILE]...
  or:  du [OPTION]... --files0-from=F
Summarize disk usage of the set of FILEs, recursively for directories.

Mandatory arguments to long options are mandatory for short options too.
  -0, --null            end each output line with NUL, not newline
  -a, --all             write counts for all files, not just directories
      --apparent-size   print apparent sizes, rather than disk usage; although
                          the apparent size is usually smaller, it may be
                          larger due to holes in ('sparse') files, internal
                          fragmentation, indirect blocks, and the like
  -B, --block-size=SIZE  scale sizes by SIZE before printing them; e.g.,
                           '-BM' prints sizes in units of 1,048,576 bytes;
                           see SIZE format below
  -b, --bytes           equivalent to '--apparent-size --block-size=1'
  -c, --total           produce a grand total
  -D, --dereference-args  dereference only symlinks that are listed on the
                          command line
  -d, --max-depth=N     print the total for a directory (or file, with --all)
                          only if it is N or fewer levels below the command
                          line argument;  --max-depth=0 is the same as
                          --summarize
      --files0-from=F   summarize disk usage of the
                          NUL-terminated file names specified in file F;
                          if F is -, then read names from standard input
  -H                    equivalent to --dereference-args (-D)
  -h, --human-readable  print sizes in human readable format (e.g., 1K 234M 2G)
      --inodes          list inode usage information instead of block usage
  -k                    like --block-size=1K
  -L, --dereference     dereference all symbolic links
  -l, --count-links     count sizes many times if hard linked
  -m                    like --block-size=1M
  -P, --no-dereference  don't follow any symbolic links (this is the default)
  -S, --separate-dirs   for directories do not include size of subdirectories
      --si              like -h, but use powers of 1000 not 1024
  -s, --summarize       display only a total for each argument
  -t, --threshold=SIZE  exclude entries smaller than SIZE if positive,
                          or entries greater than SIZE if negative
      --time            show time of the last modification of any file in the
                          directory, or any of its subdirectories
      --time=WORD       show time as WORD instead of modification time:
                          atime, access, use, ctime or status
      --time-style=STYLE  show times using STYLE, which can be:
                            full-iso, long-iso, iso, or +FORMAT;
                            FORMAT is interpreted like in 'date'
  -X, --exclude-from=FILE  exclude files that match any pattern in FILE
      --exclude=PATTERN    exclude files that match PATTERN
  -x, --one-file-system    skip directories on different file systems
      --help     display this help and exit
      --version  output version information and exit

Display values are in units of the first available SIZE from --block-size,
and the DU_BLOCK_SIZE, BLOCK_SIZE and BLOCKSIZE environment variables.
Otherwise, units default to 1024 bytes (or 512 if POSIXLY_CORRECT is set).

The SIZE argument is an integer and optional unit (example: 10K is 10*1024).
Units are K,M,G,T,P,E,Z,Y (powers of 1024) or KB,MB,... (powers of 1000).
Binary prefixes can be used, too: KiB=K, MiB=M, and so on.

GNU coreutils online help: <https://www.gnu.org/software/coreutils/>
Report any translation bugs to <https://translationproject.org/team/>
Full documentation <https://www.gnu.org/software/coreutils/du>
or available locally via: info '(coreutils) du invocation'

💡 说明:du 的核心参数分三类——统计对象(-a 全部文件、-s 只看汇总、-d/--max-depth 限深)、单位(-h、-b、-k、-m、-B、--si)、过滤(--exclude、-X、-t、-x 单文件系统)。帮助尾段说明了默认单位是 1024 字节(即 1K 一块),本文实测均基于该默认值。

2.3 版本确认

$ du --version | head -1
du (GNU coreutils) 8.32

三、参数逐项实测

3.1 默认用法:递归统计(KB 单位)

不带任何参数,du 对指定目录(缺省为当前目录)做递归统计,每个子目录输出一行:块数 + 目录名。默认单位是 1024 字节(1K):

$ cd /root/demo/du && du
9220	./data/raw
11272	./data
8	./docs
5124	./logs
16412	.

五个输出行的顺序是深度优先:先统计最深的 ./data/raw(9220K,即 users.csv 5MB + orders.csv 4MB 加目录块),再算出父目录 ./data(11272K,含 index.bin 2MB),./docs 只有 8K(一个 5 字节的文本文件加目录块),最后 . 汇总全部为 16412K。父目录的值包含其所有子目录的占用。

💡 提示:默认输出以 1024 字节为 1K(du 的 16412K 换算约 16MB)。想要 1000 进位可加 --si(帮助原文:like -h, but use powers of 1000 not 1024)。

3.2 -h:人类可读单位

-h(--human-readable)把块数换算成 K/M/G 自动选单位:

$ du -h /root/demo/du
9.1M	/root/demo/du/data/raw
12M	/root/demo/du/data
8.0K	/root/demo/du/docs
5.1M	/root/demo/du/logs
17M	/root/demo/du

与 3.1 逐行对应:9220K 显示为 9.1M(9220/1024≈9.0,加上目录块后四舍五入)、16412K 显示为 17M。日常排查建议直接 du -h,人眼读 K 数太费劲。

💡 提示:-h 以 1024 为底(M=1024K);--si 以 1000 为底(M=1000K),两者显示值会略有差异,写文档/报表时注意统一口径。

3.3 -s:只看汇总(summarize)

-s(--summarize)对每个参数只输出一行总计,不再逐层展开:

$ du -sh /root/demo/du
17M	/root/demo/du

$ du -s /root/demo/du/logs /root/demo/du/data /root/demo/du/docs
5124	/root/demo/du/logs
11272	/root/demo/du/data
8	/root/demo/du/docs

第一个示例是最常用的「看这个目录一共多大」;第二个示例对三个子目录各自给一行总计(不带 -h,单位仍是默认 K),适合横向对比几个同级目录的大小。

💡 提示:-s 等价于 --max-depth=0(帮助原文:--max-depth=0 is the same as --summarize),见 3.7。

3.4 -a:列出所有文件(不只目录)

默认 du 只输出目录行;-a(--all)把每个文件也列出来:

$ du -a /root/demo/du/docs
4	/root/demo/du/docs/notes.txt
8	/root/demo/du/docs

$ du -ah /root/demo/du/docs
4.0K	/root/demo/du/docs/notes.txt
8.0K	/root/demo/du/docs

notes.txt 内容只有 5 字节,但块分配后占 4K(1024 块数)——目录总计 8K = 文件块 4K + 目录自身块 4K。-ah 组合即「所有条目 + 人类可读」。

💡 提示:-a 与 -s 是两种视角:-a 逐条列、-s 只给总。需要「按大小排序的文件清单」时用 -a 配合 sort(见四、实战示例)。

3.5 -b: apparent size,真实字节

-b(--bytes)等价于 --apparent-size --block-size=1(帮助原文),输出文件内容的真实字节数,不再按块对齐:

$ du -b /root/demo/du/docs/notes.txt /root/demo/du/README.md
5	/root/demo/du/docs/notes.txt
7	/root/demo/du/README.md

$ ls -l /root/demo/du/docs/notes.txt
-rw-r--r-- 1 root root 5 Oct 15 06:28 /root/demo/du/docs/notes.txt

-b 显示 5 字节,与 ls -l 的 5 完全一致;而不带 -b 的默认 du(见 3.4)显示 4(即 4K 块)。-b 回答「文件有多大」,默认模式回答「文件占多少磁盘」。

💡 提示:对稀疏文件(sparse file),apparent size 可能大于磁盘占用(帮助原文说明:may be larger due to holes in ('sparse') files),两个口径谁大谁小并不固定。

3.6 --max-depth=1:限制统计深度

--max-depth=N 只输出距命令行参数 N 层以内的目录总计,更深的子层折叠进父层:

$ du -h --max-depth=1 /root/demo/du
12M	/root/demo/du/data
8.0K	/root/demo/du/docs
5.1M	/root/demo/du/logs
17M	/root/demo/du

与 3.2 相比,data/raw 这一层不再单独出现,9.1M 被并入 data 的 12M;末尾仍保留参数本身的总计 17M。「每个一级子目录各占多少」这类排查问题,--max-depth=1 一条命令就能回答。

💡 提示:--max-depth=0 与 -s 等价;--max-depth=1 保留一级子目录、2 保留两级,依此类推。短选项 -d N 同义(帮助原文 -d, --max-depth=N)。

3.7 --exclude:按模式排除

--exclude=PATTERN 不统计匹配 PATTERN 的条目。先用不带排除的输出做基线,再排除 *.log:

$ du -ah /root/demo/du/logs
2.0M	/root/demo/du/logs/error.log
3.0M	/root/demo/du/logs/app.log
5.1M	/root/demo/du/logs

$ du -ah --exclude='*.log' /root/demo/du/logs
4.0K	/root/demo/du/logs

两个 5MB 级的 .log 文件被整体排除后,logs 目录只剩目录自身的 4.0K 块。排除日志、缓存等可再生文件来评估「净数据量」时非常好用。

💡 提示:PATTERN 用 shell 通配风格(* 匹配任意串),匹配对象是路径的每一层文件名,因此 --exclude='*.log' 能命中任意目录下的日志。批量模式可写进文件用 -X, --exclude-from=FILE,按大小过滤则用 -t, --threshold=SIZE(均为帮助原文参数)。

3.8 -c:追加总计行

-c(--total)在原有输出的末尾追加一行 total:

$ du -ch /root/demo/du/logs/*
3.0M	/root/demo/du/logs/app.log
2.0M	/root/demo/du/logs/error.log
5.0M	total

$ du -c /root/demo/du/logs/*
3072	/root/demo/du/logs/app.log
2048	/root/demo/du/logs/error.log
5120	total

第一个示例对通配符展开出的两个文件逐个统计并给出合计 5.0M;第二个示例不带 -h,可见合计 5120K = 3072K + 2048K。-c 的价值在于「既要明细也要总和」的场景。

💡 提示:-c 是在其他参数行为之上追加总计行,可与 -h、-a、--max-depth 等自由组合;只想要一行总数用 -s 即可,不必 -c。

四、实战示例

4.1 分层定位大目录

磁盘告警时的标准动作:先用 --max-depth=1 看一级分布,再对最大的一层继续下钻:

$ du -h --max-depth=1 /root/demo/du
12M	/root/demo/du/data
8.0K	/root/demo/du/docs
5.1M	/root/demo/du/logs
17M	/root/demo/du

一眼看出 data 是大头(12M);继续用 -h 下钻确认细节构成:

$ du -h /root/demo/du
9.1M	/root/demo/du/data/raw
12M	/root/demo/du/data
8.0K	/root/demo/du/docs
5.1M	/root/demo/du/logs
17M	/root/demo/du

定位到 data/raw 的 9.1M(两个 CSV 文件)。

4.2 多目录横向对比

把几个候选目录一起传给 du,用 -s 各出一行总计(不带 -h 时单位统一为 K,数值可直接比大小):

$ du -s /root/demo/du/logs /root/demo/du/data /root/demo/du/docs
5124	/root/demo/du/logs
11272	/root/demo/du/data
8	/root/demo/du/docs

4.3 清理前评估:排除可再生文件

清理动作执行前,先评估「排除日志后还剩多少净数据」:

$ du -ah --exclude='*.log' /root/demo/du/logs
4.0K	/root/demo/du/logs

若要保留明细同时拿合计,用 -c:

$ du -ch /root/demo/du/logs/*
3.0M	/root/demo/du/logs/app.log
2.0M	/root/demo/du/logs/error.log
5.0M	total

4.4 与 df 对照:口径差异速查

du -sh 只统计目录树(本例 17M),df -h 统计的是整个文件系统(同一台机器的根分区 786G、已用 441G):

$ du -sh /root/demo/du
17M	/root/demo/du

$ df -h /root/demo/du
Filesystem      Size  Used Avail Use% Mounted on
overlay         786G  441G  310G  59% /

两者数字本来就不是一个口径,不能直接对账;真正的「对不上」问题(包括删了文件空间不释放)见五、注意事项 Q1。

五、注意事项与常见问题

Q1:du 和 df 看到的占用为什么对不上? 两者统计口径不同:du 沿目录树累加文件块,回答「这些文件占多少」;df 读文件系统的空闲块元数据,回答「整个分区还剩多少」。对不上有两类典型原因:一是 du 覆盖不到其他目录/其他文件系统(du 之外还有别的数据在占空间),二是已删除但仍被进程占用的文件——目录树里已经不存在,du 统计不到,df 却仍计入已用。下面实测第二种场景:先创建 40MB 文件并让一个进程持有它,再删除:

$ dd if=/dev/zero of=/root/demo/du/deleted-open.bin bs=1M count=40 status=none

$ rm /root/demo/du/deleted-open.bin

$ ls -l /root/demo/du
total 16
-rw-r--r-- 1 root root    7 Oct 15 06:28 README.md
drwxr-xr-x 3 root root 4096 Oct 15 06:28 data
drwxr-xr-x 2 root root 4096 Oct 15 06:28 docs
drwxr-xr-x 2 root root 4096 Oct 15 06:28 logs

$ du -sh /root/demo/du
17M	/root/demo/du

$ df -h / | tail -1
overlay         786G  442G  310G  59% /

文件已从目录树消失(ls 与 du 都看不到),但根分区已用反而从 441G 涨到 442G——40MB 空间仍被占用。通过 /proc 能看到持有者(266 为当时持有该文件的进程 PID,fd 100 是它打开该文件的描述符):

$ ls -l /proc/266/fd/100
lr-x------ 1 root root 64 Oct 15 06:28 /proc/266/fd/100 -> /root/demo/du/deleted-open.bin (deleted)

(deleted) 标记即「已删除但仍打开」。等该进程退出、关闭描述符后,空间立即释放:

$ df -h / | tail -1
overlay         786G  441G  310G  59% /

排查此类问题时:df 显示占用高、du 逐层加总却小一截,就去查 lsof | grep deleted 或重启对应服务。

Q2:du 统计的文件大小和 ls 看到的不一样? 默认 du 按磁盘块统计,1 字节的文件也占 1 个 4K 数据块(见 3.4:5 字节的 notes.txt 统计为 4K);ls -l 与 du -b 显示的都是内容字节数(见 3.5:同为 5)。评估「传输/下载多大」用 -b 口径,评估「占多少盘」用默认口径。

Q3:--exclude 排除后,目录本身还剩 4.0K 是什么? 排除只作用于匹配的文件,目录自身的 inode 块(ext4 上一个目录至少占一块)不受影响(见 3.7 实测剩余 4.0K)。另外 PATTERN 匹配的是文件名而不是完整路径,--exclude='*.log' 会命中所有层级下的日志。

Q4:-s 和 --max-depth=0 是什么关系? 完全等价,帮助原文明确写着 --max-depth=0 is the same as --summarize。区别只在书写习惯:-s 短且常用,--max-depth 在需要 1、2 等非零深度时才登场(见 3.3、3.6)。

Q5:-h 显示的 M 是 1000K 还是 1024K? 1024K。-h 按二进制幂(1024)换算,--si 才按十进制幂(1000)换算(帮助原文:like -h, but use powers of 1000 not 1024)。与 ls -lh、free -h 保持同一口径即可。

Q6:硬链接、符号链接会影响 du 的结果吗? 默认情况下:du 不跟随符号链接(帮助原文 -P, --no-dereference don't follow any symbolic links (this is the default)),硬链接只在首次遇到时计一次、后续重复出现的硬链接不再重复计数;如需「每个硬链接都算一遍」用 -l, --count-links,如需把命令行里的符号链接当目录展开用 -L, --dereference。本套演示数据未包含链接,此处仅依据帮助原文说明语义。

六、参考链接