如何使用每个文件的硬链接递归复制目录


52

我想创建目录树的“副本”,其中每个文件都是到原始文件的硬链接

示例:我有一个目录结构:

dirA/
dirA/file1
dirA/x/
dirA/x/file2
dirA/y/
dirA/y/file3

这是预期的结果,即目录树的“副本”,其中每个文件都是与原始文件的硬链接:

dirB/            #  normal directory
dirB/file1       #  hardlink to dirA/file1
dirB/x/          #  normal directory
dirB/x/file2     #  hardlink to dirA/x/file2
dirB/y/          #  normal directory
dirB/y/file3     #  hardlink to dirA/y/file3

Answers:


50

在Linux上(更精确地讲,在以Linux为内核的系统上通常可以找到GNU及其busybox实现cp)和最新的FreeBSD,这是这样的:

cp -al dirA dirB

有关更便携式的解决方案,请参见StéphaneChazelas的使用pax和cpio的答案。


请注意pax,在FreeBSD上,like cp -a不会硬链接符号链接。
斯特凡Chazelas

请注意,硬链接无法在单独的文件系统装载之间工作。
戴夫2015年

24

POSIXly,您将pax在读+写模式下使用以下-l选项:

pax -rwlpe -s /A/B/ dirA .

-pe保留被复制文件的所有可能的属性(在这种情况下,只有目录),如GNU cp-a一样)。

现在,尽管是标准命令,但该命令并不一定非常可移植

首先,许多基于GNU / Linux的系统pax默认情况下不包括(即使这是非可选的POSIX实用程序)。

然后,许多错误和与某些实现不符导致该代码出现许多问题。

  • 由于存在错误,与结合使用pax时,Solaris 10 (至少)不起作用。由于某种原因,它似乎将替换应用于原始路径和复制路径。因此,在上面,它将尝试做一些代替。-rwl-slink("dirB/file", "dirB/file")link("dirA/file", "dirB/file")
  • 在FreeBSD上,pax不会为符号链接类型的文件创建硬链接(POSIX允许的行为)。不仅如此,但它也适用于替代的符号链接的目标(行为通过POSIX允许的)。因此,举例来说,如果有一个foo -> AA符号链接dirA,它将成为foo -> BAdirB

另外,如果要执行相同的操作,但要使用内容存储在$src和中的任意文件路径$dst,则必须认识到pax -rwl -- "$src" "$dst"创建$srcinside 的完整目录结构$dst(必须存在并且是目录),这一点很重要。例如,如果$srcfoo/bar,则$dst/foo/bar创建。

相反,如果您想$dst成为的副本$src,最简单的方法可能是:

absolute_dst=$(umask 077 && mkdir -p -- "$dst" && cd -P -- "$dst" && pwd -P) &&
(cd -P -- "$src" && pax -rwlpe . "$absolute_dst")

(这也可以解决上述大多数问题,但是如果$dst以换行符结尾的绝对路径会失败)。

现在,在没有的GNU / Linux系统上这将无济于事pax

有趣的是,它pax是由POSIX创建的,用于合并tarand cpio命令的功能。

cpio与POSIX发明相反,它是一个历史悠久的 Unix命令(始于1977年),并且还有一个GNU实现(不是pax一个)。因此,即使它不再是标准命令(尽管它是在SUSv2中使用的),它仍然非常常见,并且通常可以依靠一组核心功能。

相当于pax -rwlcpio -pl。然而:

  1. cpio 接受stdin上输入文件的列表而不是参数(以换行符分隔,这表示不支持带有换行符的文件名)
  2. 所有文件都必须指定的(一般你给它的输出findfind并且cpio由同一人共同开发))。
  3. 元数据不保留(某些cpio实现具有保留一些但不能移植的选项)。

因此cpio

absolute_dst=$(umask 077 && mkdir -p -- "$dst" && cd -P -- "$dst" && pwd -P) &&
(cd -P -- "$src" && find . | cpio -pl "$absolute_dst")

似乎-s / A / B /特定于我的示例。如果源目录名称和目标目录名称是变量$ sourcedir和$ targetdir,您将如何做?
Gudmundur Orn 2015年

@GudmundurOrn,请参阅编辑。
斯特凡Chazelas

我在OS X上运行此命令,只是收到一条错误消息“ pax:无法将文件./a.txt链接到自身”。我从字面上使用了您的命令,只是将源目录替换为实际名称,而保留了/ A / B和最后一个点。我误会了吗?
db

@db,-s /A/B替换为AB从而dirA变为dirB。如果您的源目录名称没有A,那么它将在自身上复制(链接)它。另请参见答案的其余部分,以获取可能更好的方法。
斯特凡Chazelas


2

如果您正在寻找带有硬链接的复制功能,以查看文件(全部或部分)的快照或备份rsnapshot


1
那很有意思。但是我猜想,如果不修改文件,硬链接只是一种很好的快照机制。对?
Gudmundur Orn 2015年

@Gudmundur Orn; 这是对的。我的答案中提到的工具将以文件唯一的方式创建新快照。也就是说,现有(未修改)的文件将被创建为硬链接,而新文件(或现有文件的修改版本)将被创建为新文件。因此,结果将使冗余最少。
Janis

0

@ gudmundur-orn的回答是正确的,但是如果您使用的是Linux上的BtrFS,cp a --reflink=auto dirA dirB应该可以解决问题,区别在于文件实际上是不同的,更改一个文件不会更改另一个文件。cp -c在配备APFS的Mac上,您可以达到几乎相同的效果(auto如果不可能,-c将进行完整复制,否则将失败)。

任何COW文件系统都应该能够做到这一点,但是供应商尚未就标准命令行选项达成一致。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.