将硬链接转换为重新链接

将硬链接转换为重新链接

我有嵌套文件夹,里面有一堆文件,这些文件彼此硬链接。我想断开硬链接(将它们转换为单独的文件),然后立即将每对转换为引用链接(因此它们具有不同的 inode,但使用相同的磁盘部分)。

find -type f -links +1

将找到所有硬链接,而类似这样的命令

cp --reflink=always my_file.bin my_file_copy.bin

将复制文件而不使用任何更多磁盘空间,并将其创建为 reflink。

我如何将它们结合起来,遍历一整套嵌套文件夹,并将每个硬链接转换为引用链接,并用相同的文件名替换它们?

答案1

您标记了ubuntu,我理解您不仅限于严格的 POSIX 工具及其 POSIX 选项。

find . -type f -links +1 -execdir sh -c '
    tmp="$(TMPDIR=. mktemp)" &&
    cp -p --reflink=always -- "$1" "$tmp" &&
    mv -f -- "$tmp" "$1"
' find-sh {} \; -print

笔记:

  • 皈依者硬链接变为 reflink,即my_file.bin硬链接变为my_file.binreflink。不会有my_file_copy.bin。(此注释适用于您想要创建my_file_copy.binreflink 而保持my_file.bin硬链接不变的情况。这个问题在这件事上并不十分清楚,它my_file_copy.bin出于某种原因引入了。)
  • 如果mktempcp失败则mv不会执行。无论如何您都不应该丢失原始内容,除非其他进程修改了临时文件。
  • 由于find逐个测试文件,因此它永远不会覆盖(转换)任何 inode 的所有硬链接。如果到find那时所有硬链接都已处理-links +1,则最后一个硬链接将失败。原始 inode 将保留。这意味着如果原始文件已打开并且将就地进行修改(不更改 inode 编号),则修改将保留某处(但很难提前知道哪个硬链接将最后被处理并保留其 inode 编号)。不应该发生这样的情况:打开的文件完全解除链接,被修改并从文件系统中删除,而它一关闭。
  • 如果cpmv失败,则临时文件将保留。您可能希望将 stderr 捕获到文件 ( 2>some_file) 中,然后稍后进行调查。
  • -print如果 shell 代码成功,将会执行。它存在只是为了让您看到发生了什么。
  • find-sh解释如下:中的第二个 sh 是什么sh -c 'some shell code' sh

答案2

编辑:正如 Kamil 所指出的,不要这样做for x in $(find ...)。使用find -execdir sh -c格式是使用 find 输出的正确方法。不过,我会在这里留下我的答案。

您可以编写一个小型 Bash 脚本或直接在 bash shell 中编写 for 循环:

$ for filename in $(find -type f -links +1); do echo "I found this file: ${filename}"; done

此示例将从find命令中取出每一行并将其放置在一个${filename}变量中,然后您就可以使用它。在这里,我们只是为每个命令打印一个I found this file: $filename,但您可以用复制命令替换它,它可能看起来像这样:

$ for filename in $(find -type f -links +1); do echo "Copying ${filename} to ${filename}_copy.bin"; cp --reflink=always ${filename} ${filename}_copy.bin; done

或者,如果您想将其放入 Bash 脚本中,以便于阅读和使用。创建一个copy_script.sh包含以下内容的文件:

#!/bin/bash
for filename in $(find -type f -links +1); do
    echo "Copying ${filename} to ${filename}_copy.bin"
    cp --reflink=always "${filename}" "${filename}_copy.bin"
done

然后保存并运行$ bash ./copy_script.sh

相关内容