ディレクトリサイズの受入値はroot summary行だけにします。max-depthのchild行は内訳であり、合計すると親子を二重計上します。root行を1件だけ抽出し、独立したdu –summarizeの結果と一致した場合だけtotalとして採用します。
測定rootとrun-owned workを固定する
GNU duの割当block量をbyte単位で測ります。resultは固定fileへ上書きせずmktempへ保存し、permission errorやfilesystem errorを非0終了のまま保持します。4ブロックは同じBashセッションで実行します。
set -Eeuo pipefail
root=/srv/app/data
work_parent=/srv/lab
test -d "$root" && test ! -L "$root"
test -d "$work_parent" && test ! -L "$work_parent"
run_id=$(date -u +%Y%m%dT%H%M%SZ)-$$-$RANDOM
work=$(mktemp -d -p "$work_parent" ".ittrip-142.$run_id.XXXXXXXX")
printf '%s\n' "$run_id" > "$work/.owner"
printf 'root=%s device=%s run=%s\n' "$root" "$(stat -c %d -- "$root")" "$run_id"
root行だけを独立duと比較する
depth2 outputからpathがrootと完全一致する行を1件だけ抽出します。別processのdu –summarize –one-file-systemと一致し、両stderrが空の場合だけaccepted_root_total_bytesとします。child行は表示用breakdownに限定します。
detail="$work/data-du-depth2.txt"
detail_errors="$work/data-du-depth2.stderr"
if ! du --one-file-system --max-depth=2 --block-size=1 -- "$root" > "$detail" 2> "$detail_errors"; then
cat -- "$detail_errors" >&2
exit 30
fi
test ! -s "$detail_errors" || { cat -- "$detail_errors" >&2; exit 31; }
root_from_detail=$(awk -F '\t' -v root="$root" '$2==root { value=$1; count++ } END { if(count!=1) exit 2; print value }' "$detail")
independent_errors="$work/independent.stderr"
if ! independent_line=$(du --summarize --one-file-system --block-size=1 -- "$root" 2> "$independent_errors"); then
cat -- "$independent_errors" >&2
exit 32
fi
test ! -s "$independent_errors" || { cat -- "$independent_errors" >&2; exit 33; }
independent_total=${independent_line%%[[:space:]]*}
test "$root_from_detail" = "$independent_total"
printf 'accepted_root_total_bytes=%s\n' "$independent_total"
awk -F '\t' -v root="$root" '$2!=root { print }' "$detail" | sort -nr -k1,1 | head -n 20
nested fixtureでnaive sumを拒否する
親・子・孫を持つfixtureでは全行合計がroot totalより大きくなることを実証します。一方、root行と独立du -sは一致します。この差により、単純awk sumをacceptanceへ使っていないことを確認できます。
fixture="$work/nested-fixture"
mkdir -p -- "$fixture/a/b" "$fixture/c"
truncate --size=4096 "$fixture/a/one.bin"
truncate --size=8192 "$fixture/a/b/two.bin"
truncate --size=16384 "$fixture/c/three.bin"
du --one-file-system --max-depth=2 --block-size=1 -- "$fixture" > "$work/fixture.detail"
fixture_root=$(awk -F '\t' -v root="$fixture" '$2==root { value=$1; count++ } END { if(count!=1) exit 2; print value }' "$work/fixture.detail")
fixture_independent=$(du --summarize --one-file-system --block-size=1 -- "$fixture"); fixture_independent=${fixture_independent%%[[:space:]]*}
naive_sum=$(awk '{ sum += $1 } END { print sum+0 }' "$work/fixture.detail")
test "$fixture_root" = "$fixture_independent"
test "$naive_sum" -gt "$fixture_root"
printf 'fixture root=%s independent=%s naive_sum=%s (naive is rejected)\n' "$fixture_root" "$fixture_independent" "$naive_sum"
証拠を固定してrun-owned workを手動確認用に保持する
detail内のroot record、独立値、stderr 0 bytesを再確認し、receiptにはrootとroot-only totalだけを保存します。owner tokenと実体pathが今回のrun-owned workに一致することを読み取り確認し、fixtureと一時出力は自動削除せず手動確認用に保持します。整理が必要な場合は、receiptと保持pathを確認した後、この掲載コードとは別の明示承認を受けて実施してください。
grep -F "$(printf '%s\t%s' "$independent_total" "$root")" "$detail" >/dev/null
test "$root_from_detail" = "$independent_total"
test ! -s "$detail_errors" && test ! -s "$independent_errors"
receipt="$work_parent/.ittrip-142-$run_id.receipt"
( umask 077; set -o noclobber; printf 'root=%s\nroot_total=%s\nrun_id=%s\n' "$root" "$independent_total" "$run_id" > "$receipt" )
test "$(cat -- "$work/.owner")" = "$run_id"
work_real=$(realpath -e -- "$work")
parent_real=$(realpath -e -- "$work_parent")
case "$work_real" in
("$parent_real"/.ittrip-142."$run_id".*) ;;
(*) printf 'STOP: work ownership/path mismatch=%s\n' "$work_real" >&2; exit 40 ;;
esac
test -d "$work_real" && test ! -L "$work_real"
printf 'work_retained_for_manual_review=%s\n' "$work_real"
printf 'complete root-only total=%s; child rows are breakdown only\n' "$independent_total"
受入条件
nested fixtureでnaive_sumとroot_totalが異なり、accepted totalは独立du -sと一致します。permission-denied fixtureでは非0終了となり、部分detailを完全値としてreceiptへ保存しません。
apparent sizeとdfを分ける
duの割当量、du –apparent-size、dfのfilesystem使用量は別指標です。sparse file、hard link、削除済みopen file、snapshotにより差が出ます。目的に応じてfieldを分け、child breakdownをtotalへ再加算しないでください。
公式情報・参考資料
ディレクトリサイズ確認の構文と制約は、本文末の一次資料と対象環境のlocal helpで照合します。ディレクトリサイズ確認の記事確認日は2026年7月17日で、版が異なる場合はoption、default、終了statusの差を先に確認してください。

コメント