我的问题是,为什么-r
在复制目录时需要使用(递归)标志?即,为什么这样做:
$ cp -r dir1 copyDir1
复制目录时,什么时候不希望出现这种情况?
目录的递归副本不是真正的“默认”行为吗?我们几乎一直想要的行为?
感觉这是一个多余的标志。
我的问题是,为什么-r
在复制目录时需要使用(递归)标志?即,为什么这样做:
$ cp -r dir1 copyDir1
复制目录时,什么时候不希望出现这种情况?
目录的递归副本不是真正的“默认”行为吗?我们几乎一直想要的行为?
感觉这是一个多余的标志。
Answers:
在文件系统的工作方式中,目录实际上不是包含文件的文件夹,而是目录是包含指向与其连接的“子”文件的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项的文件,则直接复制该文件将等同于硬链接的工作方式。这不是任何人想要的。
cp -r /bin
一样容易cp-r ~/bin
。标志本身并不能防止错误,也不能使任何人都变得更谨慎。如果要防止错误,cp命令可以很容易地查看所涉及的节点并提供提示,类似“这是目录,您是否要将所有内容都复制到指定位置? / n)?” 那将是安全网。对于目录,需要-r可以使大量代码减少。
的确,这几乎是我们一直想要的行为。但是,这并不一定意味着递归复制应该是默认行为。
我认为原因cp
之所以如此,确实是源于Unix哲学。Unix偏爱能一劳永逸的程序,以及界面和实现都简单的程序(有时称为“ 越差越好”)。
难题的关键在于认识到cp
不复制目录- cp
复制文件(仅复制文件)。如果要复制目录,cp
请递归调用自身,以复制每个目录上的文件。
当然,从用户的角度来看,“复制目录”和“递归复制文件”之间的区别绝对没有,但是拥有此接口有助于实现保持简单。
如果您cp
能够复制目录,很快就会被诱使添加更多仅对目录有意义的功能-例如,您可能只希望复制以结尾的文件名.sh
。不可避免地,这会导致我们在其他操作系统中习惯于膨胀和功能蠕变 -使软件运行缓慢,复杂且容易出错。
另一个优点是,-r
它还可以帮助用户了解界面下实际发生的情况。这样做的一个很好的副作用是,当您了解支持递归运算的其他工具(grep
例如)时,学习递归运算的概念将为您节省一些工作
肯定有人会告诉您,向用户公开实现细节是不好的,而拥有更多功能则是好的。我在这里的目的仅仅是解释这种行为的原理,因此我不会尝试以任何方式争论。
与目录的交互可确保您知道自己正在与目录进行交互,而不仅仅是一个文件。
例如:
$ 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
更改默认值的结果是成千上万的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
行为。
仅当cp
是复制文件和目录的命令时才需要此标志,而不仅仅是目录。
如果有用于复制目录的特殊命令,则“默认”行为肯定是递归复制。
mkdir
呢?
正如其他人提到的,目录基本上只是另一类型的文件(与常规文件相对),通常“包含”(指向)其他文件。它可能包含子目录,同样适用于...
因此,如果要复制目录(从用户的角度来看),则实际上是在复制一堆文件(从文件系统的角度来看)(常规文件,目录文件,符号链接等),并且对于每个目录文件,您都需要递归地重复该操作处理。由于按照定义复制目录是递归过程,因此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包装器。它总是保留所有元数据(即,复制文件修改时间,正确复制符号链接等),如果您要复制目录,它将询问是否应(递归)复制它。