文件夹对磁盘使用的独特贡献

文件夹对磁盘使用的独特贡献

我有一个包含每日快照文件夹的备份。为了节省空间,不同快照中的相同文件通过硬链接(由 rsync 生成)进行重复数据删除。

当空间不足时,一种选择是删除旧快照。但由于硬链接,很难计算出删除给定快照会获得多少空间。

我能想到的一个选择是du -s首先在所有快照文件夹上使用,然后在除我可能删除的文件夹之外的所有文件夹上使用,差异将为我提供预期的获得空间。然而,这非常麻烦,当我试图找到合适的快照进行删除时,必须重复进行。

有更容易的方法吗?


尝试并思考答案后斯蒂芬·查泽拉斯德罗伯特,我意识到我的问题不够精确。这是更精确的尝试:

我有一组目录(“快照”),其中包含与另一个快照中的文件部分存储相同(硬链接)的文件。我正在寻找一种解决方案,为我提供快照列表以及其中的文件占用的每个已用磁盘存储量,但没有该存储也被另一个快照中的文件使用。我想允许每个快照中存在硬链接的可能性。

我的想法是,我可以查看该列表来决定当空间不足时应该删除哪些快照,这是删除所获得的存储空间与快照价值(例如基于年龄)之间的权衡。

答案1

您可以使用 GNU 手动完成find

find snapshot-dir -type d -printf '1 %b\n' -o -printf '%n %b %i\n' |
   awk '$1 == 1 || ++c[$3] == $1 {t+=$2;delete c[$3]}
   END{print t*512}'

它计算的是在快照目录中找到的所有链接都找到后,其链接计数将降至 0 的文件的磁盘使用情况。

find印刷:

  • 1 <disk-usage>对于目录
  • <link-count> <disk-usage> <inode-number>对于其他类型的文件。

我们假设目录的链接计数始终为 1,因为实际上并非如此,这是因为条目的原因..,并且find不列出这些条目,并且目录通常没有其他硬链接。

从该输出中,awk计算链接计数为 1 的条目的磁盘使用情况以及它所看到的<link-count>次数的 inode(即所有硬链接都在当前目录中的条目,因此,就像带有链接的条目一样) -count of one 将在目录树被删除后回收其空间)。

您还可以使用来find snapshot-dir1 snapshot-dir2找出如果两个目录都被删除,将回收多少磁盘空间(如果在两个目录中都找到并且仅在这些目录中找到文件,则这可能大于单独占用的两个目录的空间总和)快照)。

如果您想了解每次删除快照目录后会节省多少空间(以累积方式),您可以这样做:

find snapshot-dir* \( -path '*/*' -o -printf "%p:\n" \) \
  -type d -printf '1 %b\n' -o -printf '%n %b %i\n' |
   awk '/:$/ {if (NR>1) print t*512; printf "%s ", $0; next}
        $1 == 1 || ++c[$3] == $1 {t+=$2;delete c[$3]}
        END{print t*512}'

它按词汇顺序处理快照列表。如果您以不同的顺序处理它,则可能会为您提供不同的数字,除了最后一个数字(当所有快照都被删除时)。

请参阅numfmt使数字更具可读性。

假设所有文件都位于同一文件系统上。如果没有,您可以替换%i%D:%i(如果它们不全部位于同一文件系统上,这意味着您在那里有一个无论如何都无法删除的安装点)。

答案2

如果您的文件名不包含模式字符或换行符,您可以使用find+du的排除功能来执行此操作:

find -links +1 -type f \
    | cut -d/ -f2- \
    | du --exclude-from=- -s *

该位获取硬链接计数大于 1 ( )find的所有文件 ( ) 。打印出前导查找内容的修剪部分。然后询问每个目录的磁盘使用情况,不包括具有多个链接的所有文件。当然,一旦您删除了快照,现在可能存在只有一个链接的文件,而之前有两个链接 - 因此每隔几次删除,您确实应该重新运行它。-type f-links +1cut./du

如果它需要使用任意文件名,则需要更多脚本来替换du(这些是 shell 模式,因此不可能进行转义)。

此外,正如 Stéphane Chazelas 指出的那样,如果一个快照内部存在硬链接(文件的所有名称都驻留在单个快照中,而不是快照之间的硬链接),这些文件将从总数中排除(即使删除快照会恢复该空间)。

答案3

自从我写下这个答案以来,Stéphane Chazelas 就让我相信他的答案一直都是正确的。我留下我的答案,包括代码,因为它也运行良好,并且提供了一些漂亮的打印。它的输出如下所示:

              total               unique
--T---G---M---k---B  --T---G---M---k---B
     91,044,435,456          665,754,624  back-2018-03-01T06:00:01
     91,160,015,360          625,541,632  back-2018-04-01T06:00:01
     91,235,970,560          581,360,640  back-2018-05-01T06:00:01
     91,474,846,208          897,665,536  back-2018-06-01T06:00:01
     91,428,597,760          668,853,760  back-2018-07-01T06:00:01
     91,602,767,360          660,594,176  back-2018-08-01T06:00:01
     91,062,218,752        1,094,236,160  back-2018-09-01T06:00:01
    230,810,647,552       50,314,291,712  back-2018-11-01T06:00:01
    220,587,811,328          256,036,352  back-2018-11-12T06:00:01
    220,605,425,664          267,876,352  back-2018-11-13T06:00:01
    220,608,163,328          268,711,424  back-2018-11-14T06:00:01
    220,882,714,112          272,000,000  back-2018-11-15T06:00:01
    220,882,118,656          263,202,304  back-2018-11-16T06:00:01
    220,882,081,792          263,165,440  back-2018-11-17T06:00:01
    220,894,113,280          312,208,896  back-2018-11-18T06:00:01

由于我对这两个答案都不是 100% 满意(截至 2018 年 11 月 18 日)——尽管我从这两个答案中学到了——我创建了自己的工具并将其发布在这里。

如同斯蒂芬·查泽拉斯的答案,它用于find获取索引节点列表和关联的文件/目录大小,但不依赖于“最多一个链接”启发式。相反,它为每个输入目录创建一个唯一的 inode 列表(不是文件/目录!),过滤掉其他目录中的 inode,并对剩余 inode 的大小求和。这样它就可以解释每个输入目录中可能存在的硬链接。作为副作用,它忽略来自输入目录集外部的可能的硬链接。

使用的 bash 外部工具:find, xargs, mktemp, sort, tput, awk, tr, numfmt, touch, cat, comm, rm。我知道,它并不完全是轻量级的,但它完全符合我的要求。我在这里分享一下,以防其他人有类似的需求。

如果有什么可以做得更高效或者万无一失的,欢迎评论!我绝对不是一个 bash 大师。

要使用它,请将以下代码保存到脚本文件中duu.sh。第一个注释块中包含简短的使用说明。

#!/bin/bash

# duu
#
# disk usage unique to a directory within a set of directories
#
# Call with a list of directory names. If called without arguments,
# it operates on the subdirectories of the current directory.


# no arguments: call itself with subdirectories of .
if [ "$#" -eq 0 ]
then
    exec find . -maxdepth 1 -type d ! -name . -printf '%P\0' | sort -z \
        | xargs -r --null "$0"
    exit
fi


# create temporary directory
T=`mktemp -d`
# array of directory names
dirs=("$@")
# number of directories
n="$#"

# for each directory, create list of (unique) inodes with size
for i in $(seq 1 $n)
do
    echo -n "reading $i/$n: ${dirs[$i - 1]} "
    find "${dirs[$i - 1]}" -printf "%i\t%b\n" | sort -u > "$T/$i"
    # find %b: "The amount of disk space used for this file in 512-byte blocks."
    echo -ne "\r"
    tput el
done

# print header
echo "              total               unique"
echo "--T---G---M---k---B  --T---G---M---k---B"

# for each directory
for i in $(seq 1 $n)
do
    # compute and print total size
    #   sum block sizes and multiply by 512
    awk '{s += $2} END{printf "%.0f", s * 512}' "$T/$i" \
        | tr -d '\n' \
        | numfmt --grouping --padding 19
    echo -n "  "

    # compute and print unique size
    #   create list of (unique) inodes in the other directories
    touch "$T/o$i"
    for j in $(seq 1 $n)
    do
        if [ "$j" -ne "$i" ]
        then
            cat "$T/$j" >> "$T/o$i"
        fi
    done
    sort -o "$T/o$i" -u "$T/o$i"
    #   create list of (unique) inodes that are in this but not in the other directories
    comm -23 "$T/$i" "$T/o$i" > "$T/u$i"
    #   sum block sizes and multiply by 512
    awk '{s += $2} END{printf "%.0f", s * 512}' "$T/u$i" \
        | tr -d '\n' \
        | numfmt  --grouping --padding 19
    #   append directory name
    echo "  ${dirs[$i - 1]}"
done

# remove temporary files
rm -rf "$T"

相关内容