使用 bash 我需要执行 0 字节文件的查找,但在删除之前报告它们的存在

Using bash I need to perform a find of 0 byte files but report on their existence before deletion

这个问题的历史是:

我的 NAS 系统上有数百万个文件和目录。我发现有 1,095,601 个空(0 字节)文件。这些文件曾经有数据,但被没有使用正确的工具集在 XSAN 和此 Isilon NAS 之间迁移数据的前任破坏了。

这些文件是媒体制作数据,例如字体、pdf 和图像文件。除了它们存在的历史之外,它们不再有用。在我继续删除它们之前,生产用户需要记录哪些文件曾经存在,这样当他们浏览项目文件夹时,他们可以使用未受影响的文件,然后参考同一目录中记录使用过哪些文件的文本文件也在那里,从而提供某些参考文件损坏的原因。

那么如何在多个目录中查找文件并删除它们,但首先将它们的文件名输出到一个文本文件,该文本文件将保存到每个相关路径位置?

我的思路是:

for file in $(find . -type f -size 0); do
    echo "$file" >> /PATH/TO/FOUND/FILE/PARENT/DIR/deletedFiles.txt -print0 |
    xargs -0 rm ;
done

为什么不干脆

find . -type f -size 0 -exec rm -v + |
sed -e 's%^removed .\./%%' -e 's/.$//' >deletedFiles.txt

如果您的 find 太旧而无法支持 -exec ... +,您需要恢复到 -exec rm -v {} \; 或重构为

find . -type f -size 0 -print0 |
xargs -r -0 rm -v |
sed -e 's%^removed .\./%%' -e 's/.$//' >deletedFiles.txt

简短的 sed 脚本是对 rm -v 的输出进行后处理,看起来像

removed ‘./bar’
removed ‘./foo’

(文件名周围有一些有趣的引号字符)在我的系统上。当然,如果您对该输出没问题,只需从管道中省略 sed 脚本即可。

如果您事先知道哪些目录包含空文件,您可以 运行 在这些目录中单独执行上述代码段。假设您将上面的代码片段保存为名为 find-empty 的脚本(具有适当的 shebang 和执行权限),您可以简单地使用

for path in /path/to/first /path/to/second/directory /path/to/etc; do
    cd "$path" && find-empty
done

这仅在您有绝对路径时有效(如果没有,您可以 运行 通过在其周围添加括号来在子 shell 中循环的主体)。

如果您想检查树中的所有目录,请将脚本更改为打印到标准输出(从脚本中删除 >deletedFiles.txt)并尝试类似

find /path/to/tree -type d -exec sh -c '
    t=$(mktemp -t find-emptyXXXXXXXX)
    cd "" &&
      find-empty | grep . >"$t" &&
        mv "$t" deletedFiles.txt ||
        rm "$t"' _ {} \;

这使用临时文件以避免更新不包含任何空文件的目录的时间戳。 grep . 纯粹用于副作用;如果打印任何(非空)行,它将 return 成功,否则,它将报告失败;这样我们就知道要不要把临时文件移动到目标目录下了。

要删除每个空文件,同时留下一个名为 deletedFiles.txt 的文件,其中包含已删除文件的名称,请尝试:

PATH=/bin:/usr/bin find . -empty -type f -execdir bash -c 'printf "%s\n" "$@" >>deletedFiles.txt' none {} + -delete

工作原理

  • PATH=/bin:/usr/bin

    这会设置一个临时但安全的路径。

  • find .

    这开始 find 在当前目录中查找

  • -empty

    这告诉 find 只查找空文件

  • -type f

    这限制了 find 查找常规文件。

  • -execdir bash -c 'printf "%s\n" "$@" >>deletedFiles.txt' none {} +

    在每个包含空文件的目录中,这会将每个空文件的名称添加到文件 deletedFiles.txt

    注意命令中 none 的特殊用法:

    bash -c 'printf "%s\n" "$@" >>deletedFiles.txt' none {} +
    

    当此命令为运行时,bash将执行字符串printf "%s\n" "$@" >>deletedFiles.txt并将该字符串后面的参数分配给位置参数:[=25=]</code>、<code> 等。当我们使用 $@ 时,它 而不是 包含 [=25=]。它像往常一样扩展为 </code>, <code>, .... 因此,我们添加占位符 none 以便占位符被分配为 [=25=],它我们将忽略,并将完整的文件名列表分配给 "$@".

  • -delete

    这将删除每个空文件。

在@JonathanLeffler 的提示下,我已成功完成以下操作:

#!/bin/bash
## call this script with: find . -type f -empty -exec handleEmpty.sh {} +
for file in "$@"
do
  file2="$(basename "$file")"
  echo "$file2" >> "$(dirname "$file")"/deletedFiles.txt
  rm "$file"
done

这意味着我在每个相应目录的 deletedFiles.txt 标志文件中保留了已删除文件的踪迹,供用户在文件丢失时查看。这样,他们就可以继续返回存档 CD 以检索这些已删除的文件,希望这些文件不是 0 字节文件。

感谢@John1024 建议使用 empty 标志而不是 size