我有嵌套文件夹,里面有一堆文件,这些文件彼此硬链接。我想断开硬链接(将它们转换为单独的文件),然后立即将每对转换为引用链接(因此它们具有不同的 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.bin
reflink。不会有my_file_copy.bin
。(此注释适用于您想要创建my_file_copy.bin
reflink 而保持my_file.bin
硬链接不变的情况。这个问题在这件事上并不十分清楚,它my_file_copy.bin
出于某种原因引入了。) - 如果
mktemp
或cp
失败则mv
不会执行。无论如何您都不应该丢失原始内容,除非其他进程修改了临时文件。 - 由于
find
逐个测试文件,因此它永远不会覆盖(转换)任何 inode 的所有硬链接。如果到find
那时所有硬链接都已处理-links +1
,则最后一个硬链接将失败。原始 inode 将保留。这意味着如果原始文件已打开并且将就地进行修改(不更改 inode 编号),则修改将保留某处(但很难提前知道哪个硬链接将最后被处理并保留其 inode 编号)。不应该发生这样的情况:打开的文件完全解除链接,被修改并从文件系统中删除,而它一关闭。 - 如果
cp
或mv
失败,则临时文件将保留。您可能希望将 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