命令cp是copy的简写,而mv则是move的简写。那既然copy是用于实现复制文件的,那通常一般我们要指定其要复制的是谁?而且复制完以后保存在什么地方,对吧?那因此它的使用格式很简单,那就是cp srcfile dest,我这里把它简写为srcfile,src表示源文件,然后复制到目标dest,注意,这个dest我们没有指定文件,其实src也可以不用指定文件,这表示我把哪个文件从哪个源复制到什么目标去?想象一下,我们要复制一个文件,能不能把一个文件复制为另外一个文件。那能不能把一个文件复制为多个文件。把一个文件一次性的复制为多个目标可不可以,把多个文件复制成一个文件可不可以?把多个文件复制到一个目录可不可以?把多个文件复制为多个文件可不可以?按我们的理解,想象一下,哪些可以,哪些不可以。
比如像我使用cp file1 file2 file 3,你以为这是什么意思?各位要记得,或者要记得这样一个法则,对于cp命令来讲,通常只有最后一个是目标,此前的所有都是源,那就意味着它是不允许一个源多个目标,也不允许多个源多个目标,只允许出现一个目的地。那因此这里只有file3是目标,
而file1和file2则是原文件,那请问我们能否把两个文件复制为一个?可否实现?似乎不能,怎么实现把两个文件复制为一个文件呢?不能,所以一定要记得,我们要能实现的只能是复制一个文件到一个文件,或者是复制多个文件到一个目录,只能这样子了,但是我们目录下可以放多个文件,这没问题吧,所以你不能将多个文件复制的同时合并成一个文件,这是不允许的。那因此可以复制一个文件到一个文件,或者复制多个文件到一个目录,这一点一定要记得。那因此有了这样一个法则,接下来要实现复制命令就很简单了。
比如,举个例子,有个文件,有个源文件在/etc下叫passwd,我想把它复制到/tmp下,该怎么来实现呢?cp /etc/passwd /tmp是不是这样子?我指定的目标,但目标是个目录。而我复制的是个文件,这意味着什么?把/etc/passwd这个文件复制到tmp下,并且保存的那个文件名叫什么?仍然是passwd,所以如果我的目标是一个目录,并且它存在的话,那么这就意味着将原文件放在这个目录下,并保持原来的名字。再来看,cp /etc/passwd /tmp/test,我们命名成叫test,后面我又跟了个test,这会有什么结果?判断这会有什么结果?源是个文件对不对?如果目标test不存在。这就意味着我们把它复制到tmp啊下并重命名为test,如果test存在呢?要看的是目录还是文件?如果是个文件,则提示我们是不是覆盖掉?是不是这样子?那就是把test删了,原来的test删了,而且把passwd复制过来,并重命名为test,那如果test是个目录呢?OK,把passwd放在test目录下,并且保持原来的文件名,是不是这样子,所以这是个复杂的过程,大家一定要理解语言和目标的关系。那如果源是多个的话,目标就一定得是目录了,也不能不存在,必须存在,而且是个目录,否则复制将无法进行,比如,cp /etc/passwd /etc/issue /etc/inittab /tmp/abc,如下图,
他说abc is not a directory,看到了吗?他说不是一个目录,所以多个源,目标就必须得是目录了。如果我们只指定一个文件,这个问题就不复存在,
比如,我们指定/etc/inittab,/tmp下并没有叫做abc的文件,那也没关系,复制过去重命名为abc,cp /etc/inittab /tmp/abc,如下图,
没问题吧?这就是复制指定源和目标的,所以先理解这层关系,然后再来看我们cp命令,还有很多选项,这些选项有些是非常关键的,比如我们要复制一个目录,看会发生什么情况,比如我们复制/etc下的init.d,只要我们敲一下Tab键,它后面会自动补上一个斜线的,这就是个目录,我把它复制到/tmp下,并且保留原来的名字回车,结果是什么呢?cp /etc/init.d /tmp/,如下图,
叫omitting directory,它意思是略过目录,那复制过去没有啊?既然要略过,肯定是没有复制的,
/tmp下面有init.d么,没有,所以cp命令默认情况下是不会复制目录的,只复制文件。那要想复制目录的话,怎么办呢?看看cp,man cp,如下图,
有个递归选项,猜的出来,既然叫递归,叫什么?大小写的r都成,或者使用--recursive表示递归复制一个目录及其目录内的所有文件,当然也包括子目录到目标目录下,同样的道理,如果我们复制的源是目录的话,目标不存在,行不行?比如复制/etc下的init.d,我们还使用了-r选项,把它放到/tmp下叫做hello,hello不存在行不行?hello事前压根就不存在,可否?可以,把init.d复制过去,并重命名为hello,是不是这样子?这是没问题的,不存在也没问题,但如果存在而不是目录的话怎么办?或者说hello存在它是个目录,那这又是什么情况?把这个目录复制到hello下并保持原来的名字,对不对?那如果hello是个文件呢?而且存在,我们试试,比如说有个文件叫passwd对不对?我给它复制到/tmp,叫passwd,这个文件是存在的,但不是目录,结果是什么?你是否覆盖/tmp/passwd?那我们覆盖,那结果是什么?cp -r /etc/init.d /tmp/passwd,如下图,
看看passwd生成了么,那ls -l /tmp,看它的结果是什么?创建成了一个链接,而且是指向当前目录的链接文件,这其实是错误的,因为当前目录下没有这个文件,所以是覆盖不了的,一定要记得。所以看似一个简单的cp命令背后,事实上有着很多复杂的细节,好,那当然,正常复制是没有问题的。/etc/init.d到/tmp下,改命名叫hello,hello是不存在的,那没问题,复制过去就叫hello,
看一下tmp,我们有个目录叫hello,对吧?hello中是有文件的,就是init.d下的原来的文件很多,cp -r /etc/init.d/ /tmp/hello 如下图,
好,那这-r复制目录及其目录中的文件的,它实现递归复制,再看cp命令,还有很多其他选项,比如,-f表示什么?force,像我们刚才复制的时候,如果目标存在,它就会提示我们是否覆盖,对不对?-f就表示什么?强行复制,而且如果目标文件存在直接覆盖,如果目标文件不能被打开,先把它删了再重试,反正各种方法无论如何我都能把它复制过去,就这意思。还有-i选项,interactive,如果目标存在,会提示你,问你要不要覆盖,还记不记得,那事实上默认是不会提示的?但刚才我们复制的时候提示了,为什么呢?跟rm一样,看一下,实际上cp是个别名,叫cp -i看到了吗?事实上,普通用户是没有这样一个别名的,因此普通用户复制的时候,如果目标存在,就直接覆盖了,这也是红帽的系统,自己给我们自动添加的一种机制,事实上cp命令本身并没有这种能力,也就是它们本身并不是有这个别名的,就是红帽系统在安装完成以后自动生成的,其他系统未必有,各位还要理解这个概念,当然,这些东西怎么生成?将来我们都会讲到的,或者你不让它生成,安装以后把它删掉,也没有任何问题,不是什么困难的东西,如下图,
这是几个选项,我们总结一下,几个常用选项,第一个-r,递归是吧?第二个-i,做交互式,-f,强行覆盖,再看其他选项,拷贝还有一个,其他几个选项,比方说-p,指的是什么呢?如下图,
-p same as --preserve=mode,ownership,timestamps
--preserve[=ATTR_LIST]
preserve the specified attributes (default: mode,ownership,timestamps), if possible
additional attributes: context, links, xattr, all
preserve的意思就表示保留,保持的意思,在复制文件的时候保留这个文件,原有的属主、属组权限以及时间戳,mode指的就是权限,ownership指的是它的属主和属组,timestamps指的是时间戳,比如我们这样操作,我们切换到student用户,然后复制一个文件到/tmp下面,然后ls -l 看一下,如下图,
新文件的属主、属组都是student,复制的新文件就属于谁了?student,谁复杂的就属于谁了,还有文件的时间戳也变了,那我们还回来换到管理员,我们将这样一个stu.inittab给它复制成root.inittab,就在当前目标复制,cp stu.inittab root.inittab,如下图,
看,源的没动,目标改成谁的了?谁复制的新文件是不是就是属于谁建立的,所以它的属主属组就是谁的?那如果我们要使用cp -p选项,复制stu.inttab给它保存为root.inittab.2,再使用ls -l来看一下,cp -p stu.inittab root.inittab.2,如下图,
看到了么,保留了源文件的属主、属组、时间戳,这就是-p选项的作用,其实这个选项是非常有用的。除了-p之外,它还有一个选项叫-a,看什么意思?如下图,
相当于-dR和小p,它不是小p,比小p的权限还大,等于--preserve=all,意思保留所有属性的,所以-a表示保留文件所有的原来的属性,那问题是-d和-R什么意思呢?-d它说,same as --no-dereference --preserve=link,--no-dereference --preserve=link 是什么意思,--no deference是什么?先看-L选项,--deference,always follow symbolic links,总是追踪,总是跟随符号链接,什么是符号链接,这是一个快捷方式,这是一种特殊文件,这个文件实际上并不存在,它只是指向另外一个文件的路径,可以这么理解,那因此来看我举个例子,ls /etc/,如下图,
/etc/rc 是一个链接文件,那我们,cp /etc/rc /tmp ,复杂的是链接呢?还是链接指向的文件,如下图,
rc是个链接文件吗?看到前面类型就知道了,是链接吗?链接保链接保存为什么格式?是不是l,但是我们这里是l吗?不是,这是文件还是链接?显然是文件,所以默认情况下,它复制的是文件,是链接指向的那个文件,而不是链接本身,一定要记住这一点。那我们使用-L什么意思呢?再看,我们再来一次,放到tmp下,这次我们换个文件,名叫rc.2,cp -L /etc/rc /tmp/rc.2,如下图,
rc.2还是文件,那-L是什么意思呢?叫做dereference,是什么意思?另外一个跟它相反的叫做什么?no deference是不是?那no deference什么意思?这次使用-P选项,-P, --no-dereference
never follow symbolic links,
指定为/etc下的rc复制到/tmp下,叫做rc.3,这是不是又是个新文件?是叫--no-difference,cp -P /etc/rc /tmp/rc.3,那ls -l看/tmp的结果是什么?如下图,
这个反而是链接了,所以使用-P表示保持链接,如果它是个链接,我就把它复制为链接,那-L呢?或者说默认就是使用-L选项,对不对?它复制的不是链接文件,而是链接指向的那个文件,那因此,no deference什么意思呢?保持链接自有的属性,-P不就是no deference,那就意味着它复制链接的时候,就把它复制为链接,而不是复制为链接指定的文件,-L呢?复制的如果是个链接,我们事实上复制的是链接指向的文件,是这意思吧,来再看-a什么意思?指定是什么,叫-d?-d指的是什么呢?no deference,那意思是保持链接还是不保持链接?保持链接自身,复制就是链接,那再看--preserve=link,这表示事实上它就是保持链接的意思,就表示保持链接自己的,这实际上在有些时候是很有用的,我们期望复制链接属性,而不是复制链接指定的文件是很有用的,再看-a,除了-d之外,它还使用了-R,那-R是什么呢?R和r是一样的,都是recursive,保持复制,递归复制的,如果是目录,是不是也复制啊?那再往后,preserve=all,那意味着保持这个文件原有的一切属性,OK,所以-a就表示叫什么意思?叫归档存放,archive,我们不改变文件的任何属性,只是把它打包放起来,以后需要的时候还把它原封不动的还原回来,就这意思,所以它是一个备份常用的一种机制,我使用cp -a选项用于实现备份,所以这个选项非常非常关键,表示归档复制,就是常用于备份,看这样一个命令是什么意思呢?cp /etc/{passwd,inittab,rc.d/rc.sysinit} /tmp/,这个命令是干什么的?这是复制三个文件,还是复制两个文件一个目录?这实际上是三个文件,因为指的是这个目录下的文件而已,这里使用的是命令行展开,这是花括号的展开机制,那因此,这种机制事实上,我们在文件操作路径当中,很多时候都可以使用,这是cp命令。
再看下一个命令叫mv,是用来什么移动文件的对吧?移动文件的时候,它的移动方式跟cp几乎是是一样的,mv src dest,同样的道理仍然是几个相关的问题,可不可以一个源,多个目标,这是不允许的是吧?无论是cp还是mv,只有最后一个是目标,所以目标只能有一个,可以又多个源,
那可不可以一个源一个目标,可不可以多个源一个目标?多个源的时候目标存在,但不是目录行不行?比如,mv /tmp/root.inittab /var/tmp/,将/tmp/root.inittab文件剪切过去,并保留原来的名称,那如果我后面跟了个abc,但abc不存在,mv /tmp/root.inittab /var/tmp/abc,移动并重命名,再看,如果说此时我们是这样子的,mv /tmp/hello/ /var/tmp/abc,如果abc不存在的话,这意味着什么?移动hello目录,移动目录的时候不需要任何选项,这跟cp不一样,拷贝目录必须要加-R是不是,或者a,但目录不需要。所以这个命令是正常可以执行的,那因此我们mv /tmp下的hello到/var/tmp下abc,abc不存在意味着什么?把hello移动过去,并且重命名为abc,abc是目录还是文件?目录,那如果abc存在,但又是个文件怎么办?来试试,我先去我先去复这个文件,cp /etc/inittab /var/tmp/abc ,mv /tmp/hello /var/tmp/abc,如下图,
不能拿着目录去覆盖一个非目录?看到了吗?所以目标存在,如果不是目录是不允许的,如果移动的源是目录的话,那如果abc存在,并且是个目录呢?那就绕回去了,就放到这个目录下了而已,
保留原来的名字,如果abc存在并且是目录,那么就会把/tmp下的hello直接剪切到/var/tmp/abc下,并保留原来的名字。它不会将目标目录覆盖的,而仅会把它保存在目标目录下。
那看这种情况是什么意思?cd /var/tmp,mv abc mn,把当前目录下的abc移动到当前目录下,并且叫mn,这就是什么呢?重命名,如果源和目标的目录一致,就是路径一致,只是名称不同,这就是重命名,重命名不关心你是源,你是目录还是文件,只要在同一个路径下都能重命名,比如,cd /tmp,mv hello hi,直接过去改名得了,如下图,
hello就重命名成了hi了,仍然都是目录,所以我们说mv可以直接操作目录,也可以直接操作文件。type一下mv,type mv,你会发现mv其实是个别名,是mv -i,如下图,
那因此,mv也有-i选项,意味着什么呢?交互,如果目标存在,它会提示你是否覆盖的,如果不想让它提示,我们可以使用反斜线加mv是不是?还有-f表示什么?如果目标存在强制进行覆盖,是不是这样子?强制覆盖,mv还有一种非常独特的用法,还可以这么来用,它有个-t选项,man mv,如下图,
那因此,mv也有-i选项,意味着什么呢?交互,如果目标存在,它会提示你是否覆盖的,如果不想让它提示,我们可以使用反斜线加mv是不是?还有-f表示什么?如果目标存在强制进行覆盖,是不是这样子?强制覆盖,mv还有一种非常独特的用法,还可以这么来用,它有个-t选项,man mv,如下图,-t选项,这有什么用呢?使用-t,后面跟上一个目录,表示这是我们的目标,然后后面跟源,也可以这么用,用-t选项先写目标,但是要使用-t选项,后面写源,mv -t DEST SRC,也可以这样用。