计算所有不可删除文件(包括子目录中的文件)的总大小

Calculate total size of all un-deletable files (including files in subdirectories)

我有一个有点奇怪的问题,服务器上有数百个用户的共享存储。我想识别我的用户无法删除的所有文件(if 我尝试 运行 自动删除脚本)。

对于所有这些已识别的文件,我需要长列表格式(即:完整路径、所有者、上次修改时间、大小)。我目前的理解是,这将是所有直接父目录对我来说不可写的文件,如果这种做法有误,请指正。

识别出这些文件后,我还需要获取它们占用的总大小。虽然,这一步也可以通过使用简单的 python 脚本来完成,也可以添加所有尺寸,但我仍在寻找是否有更好的方法。

这是我目前的做法:

find /downloads/ -type d ! -writable -print0 |
xargs -0 -I{.} find {.} -maxdepth 1 -type f -print0 |
xargs -0 -I{.} ls -l {.} > myfile.txt
with open("myfile.txt", "r") as f:
    total_size = 0
    for line in f:
        try:
            data = line.split()
            if len(data) > 5:
                total_size += int(data[4]) # "4" is the index of the size column
        except:
            pass
print("Total Size:", str(round(total_size/(1024*1024*1024), 2)), "GB")

我在使用这种方法时遇到的问题是,我得到的最终大小是整个卷的总大小。

My answer = 1400.02GB (as reported by following the steps above)
The total size of the entire shared space = 1400.02GB
Actual occupied space on the shared server = 800GB
Expected answer should be <800GB

所以你看到的问题是我的答案等于整个服务器的总大小space,包括甚至没有被占用的space。


我的答案 (1400.02GB) 与预期答案 (<800GB) 之间的这种不匹配也让我质疑已识别文件的正确性。

有人可以建议一个更好的方法来做到这一点,或者指出我的方法中的问题以便我可以解决它。非常感谢。

以下是为什么仅添加 ls 报告的字节数来计算磁盘使用率被认为是幼稚的两个原因:

  1. 你在重复计算硬链接文件,即使硬链接只是一个目录条目,基本上不占用额外的磁盘空间space。
  2. 如果您想找出实际的磁盘使用情况(“获取它们占用的总大小”),您必须考虑块大小。例如。一个包含 1000 个 1 字节文件的目录比一个包含一个 1MB 文件的目录
  3. 占用更多space

因此,适合这项工作的工具是 du。但是为了 du 能够正确地完成他的工作而不是重复计算硬链接,您必须立即检查所有文件。幸运的是 du 的 GNU 版本有 --files0-from 选项,例如你可以使用

find . -type d ! -writable -print0 |
du -ch -d 1 --files0-from=- |
tail -n 1

这也避免了 xargs 由于 -I 选项可能会非常慢,这意味着您正在为每个参数执行 find 调用。

我不确定为什么你的命令给出了如此错误的数字,但这里有另一个你可以试试。

请注意,“它们占用的总大小” 可能与 ls 打印的大小总和有很大差异,这是由于文件系统块、硬链接等原因和稀疏文件。所以我们使用 du 而不是 ls 来获取尺寸。

下面的命令基本上是 ,但将 ls 合并到同一搜索中并使用 -exec + 而不是 xargs -I,这应该会大大加快命令速度,因为你 运行 第二个 find 只用了几次而不是每个目录一次。

#! /bin/bash

longlist() {
  ls -l "$@" >> "$longlistfile"
}
filesonly() {
  find "$@" -maxdepth 1 -type f -exec bash -c 'longlist "$@"' . {} + -print0
}
export -f longlist filesonly
export longlistfile=myfile.txt

rm -f "$longlistfile"
find . -type d \! -writable -exec bash -c 'filesonly "$@"' . {} + |
du -ch --files0-from=- | tail -n1