第04章:操作文件和目录

本章将学习以下命令

  • cp 复制文件和目录(Copy files and directories)

  • mv 移动或重命名文件和目录(Move/rename files and directories)

  • mkdir 创建目录(Create directories)

  • rm 移除文件和目录(Remove files and directories)

  • ln 创建硬链接和符号链接(Create hard and symbolic links)

以上五个命令是极其常用的 Linux 命令。它们都可以操作文件和目录两者。

现在,坦白地说,这些命令所执行的任务能够在图形界面中更简单地操作。在文件管理器中,我们能拖放一个文件到另外一个目录,剪切粘贴文件,删除文件等等。那为什么还要使用这些命令行程序呢?

答案是强大且灵活。图形界面能处理简单的文件操作,而命令行程序能更简便地执行复杂任务。例如,我们如何能从一个目录中仅仅复制目标文件夹中所没有的或者比目标文件夹更新的 HTML 文件到另一个文件夹?对于一个文件管理器来说,这个任务超级难,但是用命令行却极其简单:

cp -u *.html destination

通配符

在使用命令之前,我们需要讨论一下 shell 中能让这些命令变得更强大的一个特性。由于 shell 使用了如此多的文件名,因此提供了特殊字符来帮助我们便捷地指定一组文件名。这些特殊字符,就是通配符(wildcards)。使用通配符(也被称作 globbing),能让我们基于字符模式(patterns of characters)选择文件名。表 4-1 列出了一些通配符及其效果。

表 4-1:通配符

通配符

意义

*

匹配任意字符

?

匹配任意单个字符

[characters]

匹配组中任意单个字符

[!characters]

匹配不在组中的任意单个字符

[[:class:]]

匹配指定字符组中的任意字符

表 4-2:常用字符组

字符组

意义

[:alnum:]

匹配任意字母数字

[:alpha:]

匹配任意字母

[:digit:]

匹配任意数字

[:lower:]

匹配任意小写字母

[:upper:]

匹配任意大写字母

使用通配符,能构建复杂的文件名选择标准。表 4-3 提供了一些字符模式的样例及其所匹配的文件名。

表 4-3:通配符样例

样式

所匹配

*

所有文件

g*

以 "g" 开头的任意文件

b*.txt

以 "b" 开头的且以 ".txt" 结尾的任意文件

Data???

以 "Data" 开头,且其后跟随三个字符的任意文件

[abc]*

以 "a" 或 "b" 或 "c" 开头的任意文件

BACKUP.[0-9][0-9][0-9]

以 "BACKUP." 开头且跟随三个数字的任意文件

[[:upper:]]

以大写字母开头的任意文件

[![:digit:]]

不以数字开头的任意文件

*[[:lower:]123]

以小写字母或者 "1" "2" "3" 结尾的文件

通配符能用在所有接受以文件名为参数的命令中,我们将在第七章「如 Shell 所见的世界」中详加讨论。

字符序列

如果之前使用过类 Unix 系统或者读过关于这个主题的书,你可能遇到过 [A-Z] 和 [a-z] 这样的字符序列符号。这些传统的 Unix 符号在老版本的 Linux 中也一样正常工作。它们还能工作,但是你要小心它们不会产生你所希望的结果,除非你做了正确的配置。当前情况下,应该避免使用它们,而代之以表 4-2 所列的字符组。

在图形界面中的通配符

通配符的特殊用途,不仅在于它能在命令行中频繁使用,而且在一些图形文件管理器中也得到支持。

  • 在 Nautilus(GNOME 的文件管理器),你能使用「编辑/选择样式」菜单项来选择文件。仅输入一个文件选择样式,当前目录中符合条件的文件会高亮以供选择。

  • 某些版本的 Dolphin 和 Konqueror(KDE 的文件管理器),能在地址栏中直接输入通配符。例如,你想看所有以 "u" 开头的在 /usr/bin 目录中的文件,在地址栏中输入 /usr/bin/u* 即可。

许多基于命令行的理念也进入到图形界面。这使得 Linux 桌面更加强大。

mkdir - 创建目录

mkdir 命令用来创建目录,用法如下:

mkdir directory...

关于符号的注释:当三个句点跟随一个参数的时候,如上例所示,意味着这个参数可以被重复,因此下面的命令:

mkdir dir1

将创建一个名为 dir1 的目录。若如下:

mkdir dir1 dir2 dir3

将创建三个目录,分别是 dir1dir2dir3

cp - 复制文件和目录

cp 命令复制文件或目录。有两种不同的方法。其一:

cp item1 item2

复制单个文件或目录 item1 到文件或目录 item2。其二:

cp item... directory

复制多个项目(不论是文件或目录)到一个目录。

有用的选项及其范例

表 4-4 列出了 cp 的最常用的选项。

表 4-4 cp 选项

选项

长选项

意义

-a

--archive

复制文件及目录,以及其全部属性,包含属主和权限。通常,所复制的副本采用执行复制命令的用户的默认属性。我们将在第九章「权限」中了解文件权限。

-i

--interactive

覆盖已存在的文件前,会提示用户确认。若无此选项,cp 会静默地(即没有任何提示地)覆盖文件。

-r

--recursive

递归复制目录及目录中的内容。在复制目录时需要该选项(或 -a 选项)。

-u

--update

从一个目录复制文件到另一个目录时,仅复制那些比目标目录中更新的和于不存在目标目录的文件。这在复制大量文件时很有用,可以忽略那些不需要复制的文件。

-v

--verbose

执行复制命令时,显示详细信息。

表 4-5 cp 举例

命令

结果

cp file1 file2

复制 file1file2,如果 file2 已存在,将用 file1 的内容覆盖 file2。如果 file2 不存在,则直接创建该文件。

cp -i file1 file2

作用如上条命令。区别在于当 file2 存在时,会提示用户是否覆盖该文件。

cp file1 file2 dir1

复制 file1file2dir1,前提是 dir1 必须已经存在。

cp dir1/* dir2

使用通配符复制 dir1 中的所有文件到 dir2。前提是 dir2 必须已经存在。

cp -r dir1 dir2

复制目录 dir1 中的内容到目录 dir2。如果 dir2 不存在,则先创建该目录。复制完成后,两个目录中的内容完全相同。 若 dir2 已经存在,复制 dir1 及其内容到 dir2 中。

mv - 移动、重命名文件

mv 命令执行移动文件和重命名文件两个任务,依赖于如何使用。在任何情况下,命令执行后,原始文件都不再存在了。mv 用起来跟 cp 命令差不多:

mv item1 item2

以上命令移动或重命名文件或目录 item1item2。或如:

mv item... directory

移动一个或多个项目到另一个目录中。

有用的选项及其范例

mvcp 共享很多相同的选项,如表 4-6。

表 4-6 mv 选项

选项

长选项

意义

-i

--interactive

覆盖已存在的文件前,会提示用户确认。若无此选项,mv 会静默地(即没有任何提示地)覆盖文件。

-u

--update

从一个目录移动文件到另一个目录时,仅移动那些比目标目录中更新的和于不存在目标目录的文件。这在移动大量文件时很有用,可以忽略那些不需要移动的文件。

-v

--verbose

执行移动命令时,显示详细信息。

表 4-7 列出了 mv 命令的一些用法示例。

表 4-7 mv 示例

命令

结果

mv file1 file2

移动 file1file2,如果 file2 已存在,将用 file1 的内容覆盖 file2。如果 file2 不存在,则直接创建该文件。上述任何情况下,file1 都不复存在。

mv -i file1 file2

作用如上条命令。区别在于当 file2 存在时,会提示用户是否覆盖该文件。

mv file1 file2 dir1

移动 file1file2dir1,前提是 dir1 必须已经存在。

mv dir1 dir2

如果 dir2 不存在,则先创建该目录,而后移动 dir1 中的内容到 dir2 中,并删掉 dir1 目录。 若 dir2 已经存在,移动 dir1 及其内容到 dir2 中。

rm - 移除文件和目录

rm 命令用来移除(删除)文件和目录,用法如下:

rm item...

item 是一个或多个文件和目录。

有用的选项和示例

表 4-8 描述了一些 rm 常用的选项。

表 4-8:rm 选项

选项

长选项

意义

-i

--interactive

删除已存在的文件前,会提示用户确认。若无此选项,rm 会静默地(即没有任何提示地)删除文件。

-r

--recursive

递归删除目录。意味着被删除的目录中如果有子目录,也会被一并删除。在删除目录时需要该选项。

-f

--force

忽略不存在的文件,并且不给出提示。 此选项会覆盖 --interactive 选项。

-v

--verbose

执行删除命令时,显示详细信息。

表 4-9 提供了一些 rm 的示例。

表 4-9 rm 示例

命令

结果

rm file1

静默删除 file1

rm -i file1

作用如上条命令,区别在于删除前会给出提示。

rm -r file1 dir1

删除 file1dir1 中的内容。

rm -rf file1 dir1

作用如上条命令,区别在于如果 file1dir1 不存在,rm 命令也会继续静默执行。

谨慎使用 rm!

Linux 和类 Unix 系统没有撤销删除的命令。一旦用 rm 命令删除了的文件,就不复存在了。Linux 假设你是聪明的,你知道你在干什么。

特别需要小心的是通配符。考虑这个经典示例。你打算删掉一个目录中所有的 HTML 文件,于是你输入如下命令:

rm *.html

这是正确的,但如果意外的在 *.html 之间加了个空格:

rm * .html

rm 命令就会删除目录中所有的文件,然后反馈给你说「并没有一个叫 .html 的文件。

有用的技巧:无论何时,当你使用带通配符的 rm(同时注意你所键入的字符),请先用 ls 测试一下。这会让你看到哪些文件会被删除。然后用向上箭头键调用上次命令,把 ls 替换为 rm

ln 用来创建硬链接或符号链接。它有下面两种用法。其一创建硬链接:

ln file link

其二,创建符号链接:

ln -s item link

上述 item 可以是文件或目录。

相较于更现代的符号链接,硬链接是 Unix 原生的创建链接的方式。默认情况下,每个文件都有一个硬链接,为文件命名。当我们创建一个硬链接,我们就为一个文件创建了一个附加的目录条目。硬链接有两个重要的限制:

  1. 一个硬链接不能指向其文件系统之外的文件。意味着一个链接不能指向不在同一磁盘分区中的文件。

  2. 硬链接不能指向目录。

硬链接与文件本身无法区分。与符号链接不同,当我们列出一个含有硬链接的目录内容的时候,我们看不到关于链接的特别指示。当删除一个硬链接时,链接被移除,但其文件本身却继续存在(即其空间没有被重新分配),直到该文件所有的链接都被删除。

这对于理解硬链接非常重要,因为你可能随时会遇到他们。不过现代操作更倾向于使用符号链接,我们随后讨论。

符号链接是用来克服硬链接的限制的。符号链接创建一个特别的文件类型,包含一个文本指针,指向文件或目录。从这方面考虑,有点类似 Windows 的快捷方式,当然它们比 Windows 早了很多年。

被符号链接指向的文件,和符号链接自身,很大程度上无法区分彼此。例如,如果我们对符号链接写了些东西,也就写到了被指向的文件。当我们删掉符号链接,那仅仅是链接被删除了,文件本身没有被删除。如果文件在符号链接之前被删除,符号将继续存在,但是不指向任何文件。这种情况,称为链接被断开(broken)。在很多情况下,ls 命令用一种可区分的颜色(如红色)显示已断开的链接,以展示其存在。

链接的概念看起来很令人困惑,但是且放一边。我们打算尝试一下这些东西,希望能变得清晰一些。

建一个游乐场

因为我们打算做一些真实的文件操作,所以让我们先建造一个安全的所在去「玩」文件操作命令。首先我们需要在家目录中建一个工作用的目录,名叫 playground

创建目录

mkdir 用来创建目录。创建目录前,我们要确认是在自己的家目录中,然后再创建一个新目录。

[me@linuxbox ~]$ cd
[me@linuxbox ~]$ mkdir playground

为了让游乐场更有趣,要在目录内新建两个目录 dir1dir2。我们先切换当前目录到 playground,然后再执行一次 mkdir

[me@linuxbox ~]$ cd playground
[me@linuxbox playground]$ mkdir dir1 dir2

注意 mkdir 允许接受多个参数,能让我们在一个命令内创建两个目录。

复制文件

下一步,让我们放些数据在游乐场中。我们可以用 cp 复制文件进文件夹,来复制一个 /etc 目录中的 passwd 文件。

[me@linuxbox playground]$ cp /etc/passwd .

请留意一下我们用一个句点(.)来指代我们当前工作目录。我们可以用 ls 来看一下目录中的文件。

[me@linuxbox playground]$ ls -l
total 12
drwxrwxr-x 2 me me 4096 2018-01-10 16:40 dir1
drwxrwxr-x 2 me me 4096 2018-01-10 16:40 dir2
-rw-r--r-- 1 me me 1650 2018-01-10 16:07 passwd

现在,仅仅为了好玩,加个选项 -v 来看下:

[me@linuxbox playground]$ cp -v /etc/passwd .
'/etc/passwd' -> './passwd'

cp 命令再次执行,但是这次显示了一条简短的信息,指示正在执行的是什么操作。注意 cp 命令在覆盖上一次复制的文件时没有给出任何警告。重申一下,这是因为 cp 命令假定我们知道自己正在做什么。我们可以加入 -i 选项。

[me@linuxbox playground]$ cp -i /etc/passwd .
cp: overwrite './passwd'?

此时在提示符处回复 y 将会导致文件被覆盖,其它任意字符(例如 n)都会导致 cp 命令保留现有的文件。

移动和重命名文件

现在,passwd 这个文件名看起来不好玩,来给它改个名:

[me@linuxbox playground]$ mv passwd fun

让我们把这个重命名的文件挪到每个目录里转一圈,然后再挪回来。

[me@linuxbox playground]$ mv fun dir1
[me@linuxbox playground]$ mv dir1/fun dir2
[me@linuxbox playground]$ mv dir2/fun .

现在来看一下移动目录的效果。首先,再次把数据文件移动到 dir1 中:

[me@linuxbox playground]$ mv fun dir1

然后把 dir1 移动到 dir2 中,并用 ls 命令确认一下:

[me@linuxbox playground]$ mv dir1 dir2
[me@linuxbox playground]$ ls -l dir2
total 4
drwxrwxr-x 2 me me 4096 2018-01-11 06:06 dir1
[me@linuxbox playground]$ ls -l dir2/dir1
total 4
-rw-r--r-- 1 me me 1650 2018-01-10 16:33 fun

注意,因为 dir2 已经存在,所以 mv 命令会将 dir1 移动到 dir2 中,否则的话,将重命名 dir1dir2。最后,让我们把文件夹和文件放回原来的地方。

[me@linuxbox playground]$ mv dir2/dir1 .
[me@linuxbox playground]$ mv dir1/fun .

现在我们尝试一些链接。我们先创建一些硬链接:

[me@linuxbox playground]$ ln fun fun-hard
[me@linuxbox playground]$ ln fun dir1/fun-hard
[me@linuxbox playground]$ ln fun dir2/fun-hard

现在,对于文件 fun,我们有四个实例。来看一下目录:

[me@linuxbox playground]$ ls -l
total 16
drwxrwxr-x 2 me me 4096 2018-01-14 16:17 dir1
drwxrwxr-x 2 me me 4096 2018-01-14 16:17 dir2
-rw-r--r-- 4 me me 1650 2018-01-10 16:33 fun
-rw-r--r-- 4 me me 1650 2018-01-10 16:33 fun-hard

我们注意到, funfun-hard 的第二个字段中包含的是 4,是对于一个文件的硬链接的编号。记住,一个文件总是存在至少一个硬链接,因为文件名本身就是由链接创建的。所以我们如何知道 funfun-hard 在事实上是同一个文件?ls 命令对此不是很有帮助,我们看到这两个文件具有相同的文件大小(第五个字段),单凭这一点,也还是不能确认的。要解决这个问题,我们不得不更深入探讨一下。

当考虑硬链接时,把文件想象成两个组成部分,会很有帮助:

  1. 数据部分,包含文件内容。

  2. 名称部分,包含文件名称。

当我们创建了硬链接,我们实际上创建了一个指向同一个数据部分的附加的名称部分。系统分配一个磁盘区块的串给一个索引节点(inode),以关联名称部分。因此,每个硬链接指向一个包含特定文件内容的索引节点。

ls 命令可以调用 -i 选项来揭示这个信息。

[me@linuxbox playground]$ ls -li
total 16
12353539 drwxrwxr-x 2 me me 4096 2018-01-14 16:17 dir1
12353540 drwxrwxr-x 2 me me 4096 2018-01-14 16:17 dir2
12353538 -rw-r--r-- 4 me me 1650 2018-01-10 16:33 fun
12353538 -rw-r--r-- 4 me me 1650 2018-01-10 16:33 fun-hard

在这个版本的列表中,首字段是索引节点编号,我们能看到 funfun-hard 具有同一索引节点编号,即可确认两者为同一文件。

为了克服硬链接的以下两个短处,我们创建了符号链接:

  1. 硬链接不能跨物理设备。

  2. 硬链接不能指向目录,仅能指向文件。

符号链接是一种特殊的文件,包含指向目标文件或目录的文本指针。创建符号链接的方法类似于创建硬链接。

[me@linuxbox playground]$ ln -s fun fun-sym
[me@linuxbox playground]$ ln -s ../fun dir1/fun-sym
[me@linuxbox playground]$ ln -s ../fun dir2/fun-sym

第一个例子非常简单明了,我们加了一个 -s 选项来创建一个符号链接,而不是硬链接。但是下面两个是什么?记住,当我们创建了一个符号链接,我们创建了一个与目标文件关联的文本描述。如果用 ls,能更方便地看到:

[me@linuxbox playground]$ ls -l dir1
total 4
-rw-r--r-- 4 me me 1650 2018-01-10 16:33 fun-hard
lrwxrwxrwx 1 me me    6 2018-01-15 15:17 fun-sym -> ../fun

dir1 中的 fun-sym 的第一个字段中的 l 显示其为一个符号链接,指向 ../fun,并且是正确的。相对于 fun-symfun 处在其上级目录中。同时注意到,符号链接文件的长度是 6,是 ../fun 这串字符的长度,而非该符号链接所指向的文件的长度。

在创建符号链接时,我们也可以使用绝对路径名,如下:

[me@linuxbox playground]$ ln -s /home/me/playground/fun dir1/fun-sym

或者如前例所示用相对路径名。大多数情况下,使用相对路径名更合适,因为这样就不会因为移动或重命名符号链接所在的目录和所指向的文件所在的目录,而导致链接失效。

除了常规文件,符号链接还能指向目录。

[me@linuxbox playground]$ ln -s dir1 dir1-sym
[me@linuxbox playground]$ ls -l
total 16
drwxrwxr-x 2 me me 4096 2018-01-15 15:17 dir1
lrwxrwxrwx 1 me me    4 2018-01-16 14:45 dir1-sym -> dir1
drwxrwxr-x 2 me me 4096 2018-01-15 15:17 dir2
-rw-r--r-- 4 me me 1650 2018-01-10 16:33 fun
-rw-r--r-- 4 me me 1650 2018-01-10 16:33 fun-hard
lrwxrwxrwx 1 me me    3 2018-01-15 15:15 fun-sym -> fun

移除文件和目录

如前所述,rm 命令用来删除文件和目录。我们准备用它来清理一下游乐场。首先删掉一个硬链接。

[me@linuxbox playground]$ rm fun-hard
[me@linuxbox playground]$ ls -l
total 12
drwxrwxr-x 2 me me 4096 2018-01-15 15:17 dir1
lrwxrwxrwx 1 me me    4 2018-01-16 14:45 dir1-sym -> dir1
drwxrwxr-x 2 me me 4096 2018-01-15 15:17 dir2
-rw-r--r-- 3 me me 1650 2018-01-10 16:33 fun
lrwxrwxrwx 1 me me    3 2018-01-15 15:15 fun-sym -> fun

工作得一如预期。fun-hard 文件消失了,如第二个字段所示,fun 文件的链接计数从 4 减少到了 3。接下来我们删除文件 fun,为了有趣点,我们加了个 -i 的选项来显示它如何工作的。

[me@linuxbox playground]$ rm -i fun
rm: remove regular file 'fun'?

键入 y 以删除文件。现在来看一下 ls 的输出,注意到 fun-sym 发生了什么了?一旦符号链接指向一个现时不存在的文件,链接就破损了。

[me@linuxbox playground]$ ls -l
total 8
drwxrwxr-x 2 me me 4096 2018-01-15 15:17 dir1
lrwxrwxrwx 1 me me    4 2018-01-16 14:45 dir1-sym -> dir1
drwxrwxr-x 2 me me 4096 2018-01-15 15:17 dir2
lrwxrwxrwx 1 me me    3 2018-01-15 15:15 fun-sym -> fun

大多数 Linux 发行版会配置 ls 显示破损的链接。破损链接的存在对其自身而言不存在危险,但会显得相当混乱。如果我们尝试使用一个破损的链接:

[me@linuxbox playground]$ less fun-sym
fun-sym: No such file or directory

让我们清理一下,删除符号链接:

[me@linuxbox playground]$ rm fun-sym dir1-sym
[me@linuxbox playground]$ ls -l
total 8
drwxrwxr-x 2 me me 4096 2018-01-15 15:17 dir1
drwxrwxr-x 2 me me 4096 2018-01-15 15:17 dir2

关于符号链接要记住的一件事,大多数文件操作都是在链接的目标上执行的,而不是链接本身。rm 命令是个例外当我们删除一个链接,就是删掉链接本身,而非链接的目标。

最后,我们删掉我们的游乐场。我们要回到家目录,用 rm 加递归选项 -r 删掉 playground 及其所包含的全部文件和子目录。

[me@linuxbox playground]$ cd
[me@linuxbox ~]$ rm -r playground

在图形界面创建符号链接

GNOME 和 KDE 所提供的文件管理器,提供了一个简单且自动的方法来创建符号链接。在 GNOME,按住 Ctrl+Shift,然后拖动一个文件,就创建了一个链接,而非复制(或移动)该文件。在 KDE,当一个文件被拖动时,会显示一个小菜单以供选择是复制还是移动,或是创建一个链接。

总结

本章我们学习了不少内容,需要一段时间来消化。要一遍遍做游乐场练习来熟悉。对基本文件操作命令及通配符有个好的理解,是很重要的。在练习时,可以自由发挥,多加入一些文件和目录,使用通配符来指定多个文件,以便执行各种操作。刚开始,链接的概念会令人困扰,花些时间来学习它们是如何工作的。它们会成为真正的救生员。

扩展阅读

Last updated

Was this helpful?