为什么在Linux中复制目录时需要-r递归?


47

我的问题是,为什么-r在复制目录时需要使用(递归)标志?即,为什么这样做:

$ cp -r dir1 copyDir1

复制目录时,什么时候不希望出现这种情况?

目录的递归副本不是真正的“默认”行为吗?我们几乎一直想要的行为?

感觉这是一个多余的标志。


您也不需要复制其中的文件和文件夹吗?
QuyNguyen2013

如果您认为这会有所改善,则可以在开发人员频道上重新发布此请求。否则可能是很久以前编程的。
博主

@blogger它是很久以前编写的,但是有一个原因。这意味着,如果有人想在命令行环境中进行基本工作,那么他们的任务就应该像避免系统故障那样困难一样容易。这意味着存在一些命令行用户交互约定的充分理由。我在回答中扩展了这个概念。
JakeGould 2014年


同样适用于rm

Answers:


58

在文件系统的工作方式中,目录实际上不是包含文件的文件夹,而是目录是包含指向与其连接的“子”文件的inode指针的文件。这意味着,从文件系统的角度来看,文件是文件,但是目录只是包含连接文件列表的文件。

因此,从命令行角度来看,请执行以下操作:

$ cp dir1 copyDir1

基本上是指将名为的文件复制到名为dir1的新文件copyDir1。就文件系统而言,dir1无论如何只是一个文件。只有当文件系统实际检查dir1看看那堆比特实际上是什么时,它才是“目录”的事实。

-r标志告诉文件系统以递归方式滚动文件/目录树,并将可能是该文件“子级”的所有内容复制到新位置。

现在关于为什么看起来多余或多余的问题,这实际上归结为处理文件系统的历史性方法。以及创建一个可以避免所有类型的与用户相关的错误的系统;偶然的和故意的。

意思是,假设~/bin您的主目录中有一个文件要复制,但是由于~您是人并且会犯错误而被意外遗漏了,所以它/bin就像这样:

cp /bin/ ~/copy_of_bin

通过将“安全网” /bin作为一个目录,再加上对-r标志的需求,您可以避免将所用系统的整个二进制根意外复制到主目录中。如果不存在该安全网,则将发生较小(甚至可能是较大)的灾难。

这里的逻辑是,在GUI(图形用户界面)之前的日子里,需要设置逻辑/行为惯例,以避免用户创建可能会杀死系统的事故。-r现在使用标志是其中之一。

如果那看起来是多余的,那么只需要放在现代GUI系统之上就可以放在Linux文件系统之上。GUI通过允许用户轻松拖放文件和目录来解决此类基本用户问题。

但是,在基于文本的界面领域中,该世界中的许多“用户体验”基本上只是基于逻辑和基于幽默的路障,有助于使用户受到控制,从而避免了潜在的灾难。

同样,这就是为什么Linux / Unix文件系统没有默认设置777权限和sudo权限的原因,以及当用户设置777权限或授予所有人权限时,真正的系统管理员会畏缩的原因sudo。这些是确保系统稳定和尽可能“用户证明”的基本操作;任何急于使这些约定短路的人很可能在不知情的情况下对其系统造成损害。

附加信息: Unix Stack Exchange网站上的另一个答案很好地解释了为什么目录的非递归副本会引起问题。重点是我的。

好吧,如果没有-R标志,则只能复制文件,因为有人非递归地复制目录是很不寻常的:非递归副本只会导致该目录的第二个名称,直接指向该目录。相同的目录结构。 因为这几乎不是人们想要的,并且实际上有一个单独的程序可以执行此操作(ln),所以不允许目录的非递归副本。

因此,如果目录实际上只是其中包含inode项的文件,则直接复制该文件将等同于硬链接的工作方式。这不是任何人想要的。


19
我个人认为它的“保护”方面没有通过气味测试。有些人可以像打字cp -r /bin一样容易cp-r ~/bin。标志本身并不能防止错误,也不能使任何人都变得更谨慎。如果要防止错误,cp命令可以很容易地查看所涉及的节点并提供提示,类似“这是目录,您是否要将所有内容都复制到指定位置? / n)?” 将是安全网。对于目录,需要-r可以使大量代码减少。
JDL 2014年

12
与沙井的比喻不好。就像@JDL所说的那样,有问题的标志不会阻止路径中的错字。我会更乐意接受与其他命令的一致性,但是我感到真正的原因是“这是最初编写的方式,现在很多事情都依赖该行为,因此无法更改。”
2014年

7
当我们移动的目录,我们就不会需要-r。我认为unix.stackexchange.com上的链接答案更重要了。 等效于非递归副本将具有第二个目录,并为目录树中的所有文件提供硬链接。
gerrit 2014年

2
如果我没记错的话,-r是GNU扩展-我不认为历史上的UNIX cp 具有递归副本-这就是命令rsync的部分原因。
2014年

2
@JakeGo我是否了解基本概念。但是,我反对这样的想法,即有关命令上的-r标志可以提供任何附加的安全性。根据我的经验,命令行linux命令从本质上讲是不安全的。如果有的话,安全性已经加班了。linux设计中固有的安全性来自权限系统,并且不再以root用户身份运行。不以root用户身份运行也比设计更像是约定俗成的东西。大多数新的Linux安装程序都支持该约定,但并非总是如此。
JDL 2014年

19

的确,这几乎是我们一直想要的行为。但是,这并不一定意味着递归复制应该是默认行为。

我认为原因cp之所以如此,确实是源于Unix哲学。Unix偏爱能一劳永逸的程序,以及界面和实现简单的程序(有时称为“ 越差越好”)。

难题的关键在于认识到cp不复制目录- cp复制文件复制文件)。如果要复制目录,cp 请递归调用自身,以复制每个目录上的文件。

当然,从用户的角度来看,“复制目录”和“递归复制文件”之间的区别绝对没有,但是拥有此接口有助于实现保持简单

如果您cp能够复制目录,很快就会被诱使添加更多仅对目录有意义的功能-例如,您可能只希望复制以结尾的文件名.sh。不可避免地,这会导致我们在其他操作系统中习惯于膨胀功能蠕变 -使软件运行缓慢,复杂且容易出错。

另一个优点是,-r它还可以帮助用户了解界面下实际发生的情况。这样做的一个很好的副作用是,当您了解支持递归运算的其他工具(grep例如)时,学习递归运算的概念将为您节省一些工作


肯定有人会告诉您,向用户公开实现细节是不好的,而拥有更多功能则是好的。我在这里的目的仅仅是解释这种行为的原理,因此我不会尝试以任何方式争论。


2
+1 “……做一件事,做好事……”感谢您的陈述!
JakeGould 2014年

5

与目录的交互可确保您知道自己正在与目录进行交互,而不仅仅是一个文件。

例如:

$ tree
.
└── folder1
    └── sub1
        └── subsub1

3 directories, 0 files
$
$ cp folder1/ folder2
cp: folder1/ is a directory (not copied).
$
$ mkdir blah
$ cp blah/ blah2
cp: blah/ is a directory (not copied).
$ rm blah/
rm: blah/: is a directory

因此,如果您想成功复制一个文件夹,因为它同时暗示了该文件夹以及与引用该文件夹相关的对象,因此您必须将其视为文件的集合:

$ cp -r folder1/ folder2
$ rm -rf folder1

3

更改默认值的结果是成千上万的Shell脚本将中断。这导致POSIX和SUS对众所周知的默认行为的要求。

原因是在各种UNIX分支中cp,ln和mv命令(在大多数旧UNIX系统上都是相同的二进制文件)的历史发展。当-r出现(早期cp没有一个选项来复制目录; 这里是早期的CP手册页没有-r-R),有在处理特殊文件,符号链接和和文件系统的其他各种变幻莫测的差异。

来自开放组基础规范第7期

该标准的早期版本包括对-r选项的支持,以复制文件层次结构。-r选项是BSD和BSD派生系统上的历史惯例。POSIX.1-2008不再指定此选项,但在某些实现中可能会出现。-R选项是作为-r选项的近似同义词添加的,-r选项是为了与该版本POSIX.1-2008中所有其他递归目录下降的选项保持一致而选择的。

-R和已删除的-r选项之间的区别在于cp处理常规文件和目录文件以外的文件类型。这是由实现定义的,-选项如何处理特殊文件以允许历史实现以及那些选择支持-r的功能与POSIX.1-2008所定义的-R具有相同的功能。出于历史原因,原始的-r标志与常规文件相比,对特殊文件的处理没有任何不同,但是始终读取文件并复制其内容。在存在特殊文件类型的情况下,这显然存在问题。例如字符设备,FIFO和套接字。

实际上,您仍然会看到一些人经常使用:

cd dir1 ; tar -cf - . | (cd dir2 ; tar -xpf -)

因为他们不相信cp -r实现是他们在任意机器上习惯的;或者因为他们想要的tar行为。


3

今天的UI可能不是最理想的,但是这是在1970年UNIX设计期间某个时间做出的一个决定,当时磁盘价格要贵得多。数以百万计的shell脚本依靠这种方式工作,因此更改它为时已晚。

请参阅本文以获取原始设计信息。


3

-r标志的明显优点是,您可以cp * /target/dir仅将源目录中的所有文件复制到目标目录中,而忽略其中的所有目录(尽管有警告)。cp -r * /target/dir而是复制所有内容,包括子目录。


2

仅当cp是复制文件和目录的命令时才需要此标志,而不仅仅是目录。

如果有用于复制目录的特殊命令,则“默认”行为肯定是递归复制。


1
说得通。但是,为什么有人要复制其中至少包含一个文件的目录?为什么不那么使用mkdir呢?
JakeGould 2014年

1
@JakeGould可能是因为他们可能需要保留所有权和权限?
Ruslan

1

正如其他人提到的,目录基本上只是另一类型的文件(与常规文件相对),通常“包含”(指向)其他文件。它可能包含子目录,同样适用于...

因此,如果要复制目录(从用户的角度来看),则实际上是在复制一堆文件(从文件系统的角度来看)(常规文件,目录文件,符号链接等),并且对于每个目录文件,您都需要递归地重复该操作处理。由于按照定义复制目录是递归过程,因此cp的参数称为--recursive

当然,在用户环境中创建命令快捷方式非常容易(将其放入.profile / .bashrc文件中以使其永久可用):

alias cpr='cp -r'

也许更好:

alias cpa='cp -av'

这样,您可以使用复制目录cpa dir1 copyDir1,它不仅会打印要复制的内容,而且还会应用文件权限。

而且由于有人提到cp从理论上可以检测到源文件是一个目录并询问是否应以递归方式复制它,所以这里有一个快速建议:

cp()
{
    if [ ! -e "$1" ]; then
        echo missing source file
        return 1
    fi
    arg="-d --preserve=all -v"
    if [ -d "$1" ]; then
        read -p "Copy directory recursively? " -n 1 -r
        if [ "$REPLY" == "y" ]; then
            arg="$arg -r"
        fi
        echo
    fi
    /usr/bin/cp $arg "$@"
}

这只是一个廉价的CP包装器。它总是保留所有元数据(即,复制文件修改时间,正确复制符号链接等),如果您要复制目录,它将询问是否应(递归)复制它。

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.