目录
第一部分 shell入门 第1章 基本概念 1.1 一切都是文件 1.2 文件名的最大长度 1.3 文件名区分大小写 1.4 命名时不要使用的特殊字符 1.5 通配符及其含义 1.6 小结 第2章 基础命令 2.1 列出文件和文件夹 2.2 列出其他文件夹的内容 2.3 使用通配符列出文件夹的内容 2.4 查看子文件夹中的文件列表 2.5 以单独一列显示内容 2.6 用逗号分隔的列表显示内容 2.7 查看隐藏的文件和文件夹 2.8 显示文件的类型 2.9 用不同的颜色显示内容 2.10 显示权限、所有者等详细信息 2.11 以相反的顺序显示列表内容 2.12 按文件的扩展名排序 2.13 按日期和时间排序 2.14 按文件大小进行排序 2.15 用K、M和G显示文件大小 2.16 显示当前目录的路径 2.17 切换到不同的目录 2.18 切换到home目录 2.19 切换到以前的目录 2.20 将文件时间修改为当前时间 2.21 将文件时间修改为想要的任意时间 2.22 创建新的空文件 2.23 创建新目录 2.24 创建新目录和任何必要的子目录 2.25 看看mkdir到底做了什么 2.26 复制文件 2.27 使用通配符复制文件 2.28 复制文件时显示执行过程 2.29 防止复制时覆盖重要的文件 2.30 复制目录 2.31 复制文件到其他目录以作为完整的备份 2.32 移动和重命名文件 2.33 重命名文件和文件夹 2.34 删除文件 2.35 使用通配符一次删除多个文件 2.36 删除文件时显示执行过程 2.37 防止删除重要文件 2.38 删除空目录 2.39 删除文件和非空的目录 2.40 删除难缠的文件 2.41 变更到其他用户 2.42 变更到其他用户,包括其环境变量 2.43 变更成root用户 2.44 变更成root用户,包括其环境变量 2.45 小结 第3章 学习命令 3.1 使用man来查看命令的用法 3.2 基于命令的功能来搜索命令 3.3 根据命令的名称快速查找命令的功能 3.4 重建命令的man数据库 3.5 读取命令的特定man page 3.6 打印man page 3.7 学习info命令 3.8 在Info页面中导航 3.9 查找命令的可执行文件、源文件和man page的路径 3.10 读取命令的描述 3.11 基于功能查找命令 3.12 找出将要运行的命令的版本 3.13 小结 第4章 组合命令 4.1 连续运行多个命令 4.2 只有前面的命令运行成功,才运行下一个命令 4.3 只有前面的命令运行失败,才运行下一个命令 4.4 将一个命令的输出插入到另一个命令 4.5 理解输入/输出流 4.6 将一个命令的输出用作另一个命令的输入 4.7 将命令的输出重定向到文件 4.8 防止重定向时覆盖文件 4.9 将命令的输出追加到文件 4.10 将文件作为命令的输入 4.11 小结 第二部分 使用文件 第5章 查看文件 5.1 在标准输出设备上查看文件 5.2 将文件拼接至标准输出设备 5.3 将文件与其他文件拼接 5.4 拼接文件,并给文件加上行号 5.5 分屏查看文本文件 5.6 在分页器中搜索 5.7 在分页查看文件时进行编辑 5.8 查看文件的前10行内容 5.9 查看多个文件的前10行内容 5.10 查看一个或多个文件的前几行内容 5.11 查看文件前几个字节、几K字节或几M字节的内容 5.12 查看文件的最后10行内容 5.13 查看多个文件的最后10行内容 5.14 查看一个或多个文件的后面几行内容 5.15 查看一个或多个文件中不断更新的最后几行 5.16 小结 第6章 打印和管理打印任务 6.1 列出所有可用的打印机 6.2 找到默认的打印机 6.3 查看打印机是如何连接的 6.4 一次性获取打印机的所有信息 6.5 将文件打印到默认的打印机 6.6 将文件打印到任何打印机 6.7 打印多份文件 6.8 列出打印任务 6.9 按打印机来列出打印任务 6.10 取消发送到默认打印机上的当前打印任务 6.11 取消发送到任何打印机上的打印任务 6.12 取消所有的打印任务 6.13 小结 第7章 拥有者和权限 7.1 修改文件或目录属于的用户组 7.2 递归修改目录属于的用户组 7.3 使用chgrp命令查看文件用户组的变化 7.4 修改文件和目录的拥有者 7.5 同时修改文件和目录的拥有者和用户组 7.6 理解权限的基础 7.7 用字母表示法修改文件和目录的权限 7.8 用数字权限修改文件和目录的权限 7.9 递归地修改权限 7.10 设置和清除suid 7.11 设置和清除sgid 7.12 设置和清除sticky bit 7.13 小结 第8章 归档和压缩 8.1 用zip归档和压缩文件 8.2 用zip获得最好的压缩效果 8.3 用密码保护压缩的Zip文档 8.4 解压文件 8.5 列出将要解压的文件 8.6 测试将要解压的文件 8.7 用gzip归档和压缩文件 8.8 用gzip递归式地归档和压缩文件 8.9 用gzip获得最好的压缩效果 8.10 解压用gzip压缩的文件 8.11 测试将要用gunzip解压的文件 8.12 用bzip2归档和压缩文件 8.13 用bzip2获得最好的压缩效果 8.14 解压用bzip2压缩的文件 8.15 测试将要用bunzip解压的文件 8.16 用tar归档文件 8.17 用tar和gzip归档和压缩文件 8.18 测试将要解开和解压的tarball 8.19 解开和解压tarball 8.20 小结 第三部分 查找资料 第9章 查找资料:就这么简单 9.1 搜索文件名数据库 9.2 搜索文件名数据库,不区分大小写 9.3 管理搜索文件名数据库时返回的结果 9.4 更新locate使用的数据库 9.5 在文本文件中搜索匹配的模式 9.6 在文本文件中搜索特定模式的基础知识 9.7 递归式地搜索文件中的文本 9.8 搜索文件中的文本,忽略大小写 9.9 在文件中只搜索整个词 9.10 显示搜索结果在文件中的行号 9.11 在其他命令的输出中搜索特定内容 9.12 查看文件中搜索内容的上下文信息 9.13 显示没有包含搜索内容的行 9.14 列出包含搜索内容的文件名 9.15 在搜索结果中进行搜索 9.16 小结 第10章 find命令 10.1 根据文件名搜索文件 10.2 根据拥有者搜索文件 10.3 根据用户组搜索文件 10.4 根据文件大小搜索文件 10.5 根据文件类型搜索文件 10.6 当表达式均为true时显示结果(AND) 10.7 当表达式中只有一个为true时就显示结果(OR) 10.8 当表达式为not true时显示结果(NOT) 10.9 对搜索到的每个文件执行命令 10.10 将搜索结果打印到文件 10.11 小结 第四部分 环境 第11章 shell 11.1 查看命令行历史 11.2 再次运行最近运行过的命令 11.3 使用数字再次运行以前运行过的命令 11.4 使用字符串再次运行以前运行过的命令 11.5 显示所有命令的别名 11.6 查看特定命令的别名 11.7 创建新的临时别名 11.8 创建新的永久别名 11.9 删除别名 11.10 小结 第12章 监视系统资源 12.1 查看当前正在运行的所有进程 12.2 查看进程树 12.3 查看特定用户拥有的进程 12.4 终止正在运行的进程 12.5 查看正在运行的进程的动态更新列表 12.6 列出打开的文件 12.7 列出某个用户打开的文件 12.8 列出正在使用特定文件的用户 12.9 列出特定程序的进程 12.10 显示系统RAM的信息 12.11 显示文件系统的磁盘使用情况 12.12 报告目录使用的文件空间 12.13 只报告目录使用的总空间 12.14 小结 第13章 安装软件 13.1 为基于RPM的Linux系统安装软件 13.2 删除基于RPM的Linux系统中的软件包 13.3 为基于RPM的Linux系统安装软件包及其依赖程序 13.4 删除基于RPM的Linux系统中的软件包及其依赖程序 13.5 升级基于RPM的Linux系统中的软件包及其依赖程序 13.6 为基于RPM的Linux系统查找可供下载的软件包 13.7 为Debian安装软件包 13.8 删除Debian中的软件包 13.9 为Debian安装软件包及其依赖程序 13.10 删除Debian中的软件包及其依赖 13.11 升级Debian中的软件包及其依赖 13.12 为基于Debian的Linux系统查找可供下载的软件包 13.13 清除Debian中不再需要的安装包 13.14 处理apt相关的错误 13.15 小结 第五部分 网络 第14章 连接 14.1 查看网络接口状态 14.2 验证计算机是否正在运行和能否接收请求 14.3 跟踪数据包在两台主机之间经过的路由 14.4 执行DNS查询 14.5 配置网络接口 14.6 查看无线网络接口的状态 14.7 配置无线网络接口 14.8 使用DHCP获得新的网络地址 14.9 启动网络连接 14.10 关闭网络连接 14.11 显示IP路由表 14.12 修改IP路由表 14.13 解决网络问题 14.14 小结 第15章 使用网络 15.1 安全登录到另一台计算机 15.2 不用密码安全登录到另一台计算机 15.3 在计算机之间安全地传输文件 15.4 在主机之间安全复制文件 15.5 安全传输和备份文件 15.6 非交互式地下载文件 15.7 非交互式地下载整个网站 15.8 顺序下载多个文件和因特网资源 15.9 小结 第16章 Windows联网 16.1 查找工作组的主浏览器 16.2 NetBIOS名称和IP地址的查询和映射 16.3 列出机器上的Samba共享 16.4 用类似FTP的客户端访问Samba资源 16.5 挂载Samba文件系统 16.6 小结 目录

Linux命令速查

Linux命令大全


第一部分 shell入门

第1章 基本概念

第2章 基础命令

第3章 学习命令

第4章 组合命令



第1章 基本概念

在真正深入学习bash shell之前,首先需要搞清楚一些概念,这有助于继续本书的学习。这些概念绝对是你必须知道的,而且有些概念并不显而易见。但是当理解它们以后,你的shell命令也将更加有的放矢。

在Linux系统中,一切都是文件。说到一切,首先就是那些看起来明显是文件的东西。当然,文本文档是文件,OpenOffice.org文档也是文件。别忘了,图片、MP3和视频,它们都是文件。这是毫无疑问的!

但是目录呢?它也是一种文件,只不过是一种特殊的文件,其中包含其他文件的信息。磁盘驱动器则是真正的大文件了。网络连接也是文件,甚至运行中的进程都是文件。这些都属于文件的范畴。

对于Linux来说,文件只是比特和字节流。Linux并不关心这些二进制位和字节最终组成了什么格式;相反,在Linux上运行的程序才关心它们组成的格式。对于Linux来说,文本文档和网络连接都是文件;而文本编辑器知道如何使用文本文档,因特网应用程序则可以识别网络连接。

本书随处都会提到文件。读者可以根据上下文将“文件”理解为“文件、目录、子目录以及系统中的其他所有东西”。特别是我提到的很多命令,它们对文档和目录都一样有效,可以随意在文档和目录上测试这些命令。

曾经使用过MS-DOS的人们应该记得,DOS中文件名的长度不能超过8个字符,再加上由3个字符组成的扩展名,这种命名方法得出的名字描述性很差,例如MSRSUME1.DOC。OS X之前的苹果机系统则将长度限制扩展到31个字符,虽然这样的文件名似乎应该够长了,但是仍然可能会导致一些稀奇古怪的名字。

Linux(和Unix)文件名最多可以长达255个字符。对于文件名来说,这有些过长。如果你真用了这么长的文件名的话,那界面看起来就跟字典一样烦琐了。有了长达255个字符的空间,就可以自由地将文件名起得更加具有描述性也更准确——当然,过犹不及。

事实上,将文件名的长度控制在80个字符以内比较好,因为大部分显示终端都是这个宽度,文件名能在一行显示完,不用换行。不过,这只是一个建议,不一定非得这样。你有权利去用200个以上的字符来描述一个文件,只是你应该明智地行使这一权利。

与Windows和Mac OS不同,Linux对于文件名是区分大小写的。在运行Linux的计算机上,同一目录中可能存在以下3个文件:

  • bookstobuy.txt

  • BooksToBuy.txt

  • BoOkStObUy.txt

对于Linux文件系统来说,它们是3个完全不同的文件。但是在Windows或者Mac OS上,如果想在已经包含有bookstobuy.txt的目录中增加另一个BooksToBuy.txt文件,系统就会要求你重新命名这个文件,或者取消这一操作。

区分大小写也意味着必须准确输入命令和文件名。例如,如果要运行rm命令来删除文件,就不能输入RM、Rm或者rM,只能输入rm。如果原本要删除的是bookstobuy.txt,结果输入的是rm BooksToBuy.txt,那么就会错删了别的文件,或者输入的文件根本就不存在。

这一教训是双重的:Linux强制要求你必须精确,而精确性确实是个好东西。同时,Linux也给了你一定程度的灵活性,这在其他操作系统中是找不到的。精确性与灵活性的结合,是让使用Linux变得有趣的一个原因,但同时也会给新手带来一些困惑。

当为文件和目录命名时,每种操作系统都会有一些禁止使用的字符。例如,在Mac OS中不允许使用冒号();而Windows中则不能使用反斜杠字符(\)。Linux中也有一些禁止使用的字符。但是在介绍这些字符之前,我们先来看看哪些字符总能安全使用:数字、字母(大写字母或小写字母)、圆点(.)以及下划线(_)。键盘上的有些其他字符也可以正常使用;有些虽然能够使用,但实际情况则比较复杂,因为shell对它们的解释方法可能各不相同;而还有一些则根本不能使用。

/”字符就永远不能使用,因为它是用于分隔目录和文件的特殊字符。假设你想用一个文件来列出需要购买的图书,而且想方设法地将文件命名为books/to_buy.txt(带“/”),以便将它与books/on_loan.txt和books/lost.txt区别开。现在当你想引用位于/home/scott/documents/books/to_buy.txt的这一文件时,命令就不能正常运行,因为shell会以为documents目录中有个books目录,但实际上这个目录是不存在的。

可以用下划线来代替斜线(如books_to_buy),或者将单词都挤在一起(例如booksToBuy.txt或BooksToBuy.txt)。

可以使用连字符(“-”),写成books-to-buy.txt这样。但是我觉得下划线要更好些,因为分隔单词时下划线没有连字符那么碍眼。不过,如果要使用连字符,不要把它放在文件名的开始(例如-books_to_buy.txt),也不要把它放在空格的后面(例如books - to buy)。稍后可以看到,当使用命令时,可能会调用该命令的特殊选项,而在选项前面就需要加上连字符。第2章会介绍用rm命令删除文件,但是如果你输入的是rm -books_to_buy.txt,shell将显示以下的错误信息:

rm: invalid option -- b

如果你愿意,也可以在文件名中使用空格,如books to buy.txt,但是必须得让shell知道这些空格是文件名的一部分。shell通常是将空格作为参数之间的分隔符。试图直接删除books to buy.txt会让shell产生误解,它尝试先删除名为books的文件,然后删除名为to的文件,最后删除名为buy.txt的文件。最终的结果是你不能删除books to buy.txt这个文件,反而可能无意间删除那些原本不想删除的文件。

那么如何处理文件名中的空格呢?还有“*”和“?”这些下一节要学习使用的字符呢?还有“'”和“"”字符,它们在shell中有特殊的含义,能直接在文件名中使用吗?你可以有多种选择。如果可能的话,尽量不要使用这些字符。或者在这些字符前面加个“\”字符来对它们进行转义,这样就告诉shell:应该忽略这些字符的特殊用法,将它们作为简单字符来对待。然而,转义字符也很麻烦,一定要确保在任何时候都将“\”字符放到正确的位置上。

$ rm Why\ don\ 't\ I\ name\ files\ with\ \*\?.txt

嗯!还有个更简单的方法,麻烦也少点,就是用引号把文件名括起来,功能类似于“\”:

$ rm "Why don't I name files with *?.txt"

这种方法可行,但总是必须使用引号仍旧很痛苦。更好的选择就是根本不使用这些字符。表1-1列出了一些特殊字符以及它们的用法。

表1-1 在文件名中如何使用特殊字符
字  符建  议
/绝对不能使用。不能被转义
\必须转义。避免使用
-不能作为文件名或目录名的开始
[ ]必须转义。避免使用
{ }必须转义。避免使用
*必须转义。避免使用
?必须转义。避免使用
'必须转义。避免使用
"必须转义。避免使用

假设你的计算机目录中有下列12个图片文件和1个文本文件:

libby1.jpg
libby2.jpg
libby3.jpg
libby4.jpg
libby5.jpg
libby6.jpg
libby7.jpg
libby8.jpg
libby9.jpg
libby10.jpg
libby11.jpg
libby12.jpg
libby1.txt

你想在命令行中使用rm命令(第2章介绍)来删除这些文件,一次删除一个文件的做法既烦琐,还有些愚蠢。毕竟,使用计算机的一个原因就是它能够自动处理和简化乏味的任务。通配符可以完成这一任务,它可以通过字符匹配一次指定多个文件。

有3种通配符:*(星号)、?(问号)、和[ ](方括号)。接下来我们依次看看它们的用法。

*”匹配0或多个任意字符。表1-2列举了“*”的一些用法以及能够匹配的内容。

表1-2 “*”通配符及其匹配的内容
命  令匹  配
rm libby1*.jpglibby10.jpglibby12.jpg,但不匹配libby1.txt
rm libby*.jpglibby1.jpglibby12.jpg,但不匹配libby1.txt
rm *txtlibby1.txt,但不匹配libby1.jpglibby12.jpg
rm libby*libby1.jpglibby12.jpg,以及libby1.txt
rm *目录中的所有文件

?”匹配单个字符。表1-3列举了“?”的一些用法,以及能够匹配的内容。

表1-3 “?”通配符及其匹配的内容
命  令匹  配
rm libby1?.jpglibby10.jpglibby12.jpg,但不匹配libby1.txt
rm libby?.jpglibby1.jpglibby9.jpg,但不匹配libby10.jpg
rm libby?.*libby1.jpglibby9.jpg,以及libby1.txt

[ ]”可以匹配一组单个字符(例如,[12]),或者是匹配用连字符(“-”)指定的某一范围内的字符(例如,[1-3])。表1-4列举了“[ ]”的一些用法,以及能够匹配的内容。

表1-4 “[ ]”通配符及其匹配的内容
命  令匹  配
rm libby1[12].jpglibby11.jpglibby12.jpg,但不匹配libby10.jpg
rm libby1[0-2].jpglibby10.jpglibby12.jpg,但不匹配libby1.jpg
rm libby[6-8].jpglibby6.jpglibby8.jpg,但不匹配其他文件

本书中使用了很多通配符,所以现在介绍一下它们的用法是很有必要的。它们使在命令行中处理文件容易了很多,你一定会发现它们是多么有用。

本章介绍的这些Linux内容,其意义对于新手来说可能并不那么明显,但是当开始使用shell和它的命令时,它们就会派上用场。当你开始使用后续章节介绍的命令时,本章讲的这些细节能够帮你避免很多麻烦。

思考一下,为什么目录中包含空格时,就不能复制它?如何一次删除1 000个文件?或者,为什么不能运行RM bookstobuy.txt?这些都不是有趣的问题。预先了解一些基础知识,就能够避免犯那些令很多人痛苦不堪的常见错误。

扫除了这些障碍以后,接下来就可以学习命令行了。翻到下一页,我们开始吧!



第2章 基础命令

本章介绍几乎每天都会用到的基础命令。这些命令就像锤子、螺丝刀、锯等木匠经常用的工具,需要放在工具箱的最上面。学会了这些命令以后,你就可以控制shell,对文件、文件夹、数据和环境执行各种有意思的操作。

ls

ls命令应该是人们使用次数最多的一个命令。毕竟,在处理和使用目录中的文件之前,必须先知道目录中有哪些文件。这就是ls命令发挥作用的地方,因为它能够列出目录中的文件和子目录。

说明 ls命令听起来可能很简单,不就是显示有哪些文件嘛!但是我们在后面将会看到,这个命令的选项组合多得令人吃惊,也使它表现出惊人的灵活性。

输入ls命令会列出当前所在目录的内容。第一次登录shell时,当前目录是你的home目录。输入ls,可能会看到以下内容:

$ ls
alias Desktop   iso   pictures program_files todo
bin   documents music podcasts src           videos

ls music

要查看某个目录的内容,不必先转到那个目录。假设你现在位于home目录,但是想要看看music目录中有什么内容,那么只需要输入ls命令,后面再跟上你想要查看其内容的文件夹名称,如下所示:

$ ls music
Buddy_Holly  Clash  Donald_Fagen  new

在上一个例子中使用的是相对路径,但绝对路径也同样有效。

$ ls /home/scott/music
Buddy_Holly  Clash  Donald_Fagen  new

如果你不喜欢每次查看某个目录的内容时,还得在文件系统的各个目录之间来回切换,那么能够指定相对路径或绝对路径就非常方便了。不确定你是否还有Tiger Woods完美的推杆击球动作的视频,尝试一下下面的命令(“~”字符就像一个别名,代表你的home目录):

$ ls ~/videos
Ubuntu_Talk.mpeg      nerdtv_1_andy_hertzfeld
airhorn_surprise.wmv  nerdtv_2_max_levchin
apple_navigator.mov   nerdtv_3_bill_joy
b-ball-e-mail.mov     RPG_Nerds.mpeg
carwreck.mpg          tiger_woods_just_did_it.wmv

对,就是这个视频文件:tiger_woods_just_did_it.wmv。

ls ~/videos/*.wmv

刚才你已经学会了如何在包含多个文件的目录中查找文件,但是还有一种更快的方法。如果你知道正在找的Tiger Woods的视频文件是Windows Media格式的,那么这个文件名一定是以.wmv为后缀的,这时就可以使用通配符只显示以.wmv结尾的文件。

$ ls ~/videos
Ubuntu_Talk.mpeg       nerdtv_1_andy_hertzfeld
airhorn_surprise.wmv   nerdtv_2_max_levchin
apple_navigator.mov    nerdtv_3_bill_joy
b-ball-e-mail.mov      RPG_Nerds.mpeg
carwreck.mpg           tiger_woods_just_did_it.wmv
$ ls ~/videos/*.wmv
airhorn_surprise.wmv   tiger_woods_just_did_it.wmv

另外还有一种更快的方法,也使用了通配符:只查找文件名包含tiger这个单词的文件。

$ ls ~/videos/*tiger*
tiger_woods_just_did_it.wmv

ls -R

也可以用一个命令来查看多个子目录的内容。假如你正在参加一个LUG(Linux Users Group)聚会,大家都在紧张地安装着Linux。有人说话了:“嗨,谁有新的Kubuntu的ISO镜像文件,能给我用用吗?”你记得前几天下载过这么个文件,为了确定一下,运行以下命令(除了用ls–R,也可以用ls --recursive):

$ ls -R ~/iso
iso:
debian-31r0a-i386-netinst.iso knoppix ubuntu

iso/knoppix:
KNOPPIX_V4.0.2CD.iso  KNOPPIX_V4.0.2DVD.iso

iso/ubuntu:
kubuntu-5.10-install.iso   ubuntu-5.10-install.iso
kubuntu-5.10-live.iso      ubuntu-5.10-live.iso

就是这个文件,在~/iso/ubuntu : kubuntu-5.10-install-i386.iso中。-R选项会递归地遍历iso目录,显示iso目录和它的每个子目录的内容。每个文件夹都以原命令中指定的目录作为开始,显示为它们各自相对于该目录的路径,路径名称后面跟着一个冒号,接着再列出相应子文件夹中的所有内容。记住,如果有很多子目录,子目录中又有很多内容的话,递归选项就不太起作用。因为显示的内容将会一屏接着一屏,很难找到你想要的内容。当然,如果你想做的只是验证一下目录中确实有很多文件和文件夹,这时滚动浏览所有内容是很有用的,不过这种情况并不经常有。

ls -1

到目前为止,你使用的都是ls命令的默认输出格式。注意,ls命令按照字母顺序列出目录的内容,每列之间最少保留两个空格,以便阅读。但是如果你想要用不同的方式来查看内容又该怎么做呢?

如果多列显示方式不是你想要的,从逻辑上来讲,你也可以用单独一列的方式来查看ls命令的结果,这时用ls-1(或ls --format= single-column)就够用了。

$ ls -1 ~/
bin
Desktop
documents
iso
music
pictures
src
videos

如果目录中的内容多得数不清,这种列举方式可能会变得没完没了,尤其是使用递归选项时更是如此,例如ls -1R ~/。如果看到终端屏幕上没完没了地显示文件列表,这时可以按Ctrl+c组合键取消命令。

ls -m

不论是一列还是多列,对于不喜欢标准显示方式的人来说,另外一种显示格式选择就是-m选项(或--format=commas)。

$ ls -m ~/
bin, Desktop, docs, iso, music, pix, src, videos

-m中的m看作是“,(逗号)”的助记符,这样更容易记住这个选项。当然,如果你正在编写脚本,需要以逗号分隔的列表来表示目录的内容时,这个选项也很有用。不过,这是这个有价值的选项更为高级的一种用法。

ls -a

现在,你已经能够查看目录中可见的文件了,但是不要忘记,很多目录中还包含隐藏的文件。例如,home目录就有大量的隐藏文件和文件夹,都是在它们的名字前面加上一个“.”,就设置成了不可见的。如果想查看这些隐藏文件,只要使用-a选项(或--all)。

$ ls -a ~/
.            .gimp-2.2      .openoffice.org1.9.95
..           .gksu.lock     .openoffice.org1.9
.3ddesktop   .glade2        .openoffice.org2
.abbrev_defs .gnome         .opera

.adobe       .gnome2_private pictures

关于这个列表有几点应该知道。首先,ls –aa代表all(所有)]命令既能显示隐藏内容,也能显示非隐藏内容,所以.gnomepictures这两个文件都能看到。其次,你总是可以看到“.”和“..”,因为“.”代表当前目录,而“..”则代表上一级目录,即当前目录的父目录。这两个隐藏文件在系统的每个文件夹中都存在,不能删除。每次使用-a选项时就能看到它们。最后,根据不同的目录,-a选项可能显示出你以前没有注意到的大量隐藏文件。

ls -F

除了目录中各文件的名称以外,ls命令默认不会告诉你更多其他的信息。仅凭名称,很难区分清楚某项是文件、目录,或是其他什么东西。为了解决这一问题,可以让ls命令提供更多的信息,一种简单方法就是使用-F选项(或--classify)。

$ ls -F ~/bin
adblock_filters.txt    fixm3u*        pix2tn.pl*
addext*                flash.xml*     pop_login*
address_book.csv       getip*         procmail/
address_book.sxc       homesize*
programs_kill_artsd*
address_book.xls       html2text.py*
programs_usual*

这样显示的信息就多了些。文件后面的“*”(星号)表示该文件是可执行文件,“/”(斜杠)表示它是一个目录。如果文件名后面没有任何附加符号,那这个文件就是一个普通的文件。表2-1总结了一些其他可能的结尾附加符号。

表2-1 符号和文件类型
字  符含  义
*可执行文件
/目录
@符号链接文件
|管道(FIFO)
=套接字(socket)

ls --color

除了使用-F选项在文件和文件夹名称后面附加特殊的符号,还可以让shell用不同的颜色显示内容,这样就能够用另一种方法把不同的内容进行分类,将它们区分开来。很多Linux系统在安装时就已经设置好shell显示不同的颜色,但是如果你的shell还没有设置好,就得使用--color选项。

$ ls --color
adblock_filters.txt   fixm3u      pix2tn.pl
addext                flash.xml   pop_login
address_book.csv      getip       procmail

在我安装的系统中,可执行文件显示为绿色,文件夹显示为蓝色,普通文件显示为黑色(在我的shell中,文本的默认颜色是黑色)。表2-2列举了所有常见的颜色关联(但要记住,特定的Linux发行版本中这些颜色的含义可能会有所不同)。

表2-2 颜色和文件类型
颜  色 含  义
默认的shell文本颜色 普通文件
绿色 可执行文件
蓝色 目录
紫红色 符号链接文件
黄色 管道(FIFO)
紫红色 套接字(socket)
红色 压缩文件(.tar.zip.deb.rpm
紫红色 图片文件(.jpg.gif.png.tiff
紫红色 音频文件(.mp3.ogg.wav

提示 知道系统为各种文件映射了什么颜色吗?输入dircolors --print-database,自己慢慢看结果吧。也可以使用dircolors命令修改这些颜色配置。

结合--color-F选项,当前目录中有哪些类型的文件一目了然。现在我们就来试一把!

$ ls -F --color
adblock_filters.txt       fixm3u*        pix2tn.pl*
addext*                   flash.xml*     pop_login*
address_book.csv          getip*         procmail/

ls -l

现在你已经学会了如何格式化ls命令的结果,以获取更多关于目录内容的信息,但是实际内容本身的情况会怎么样呢?如何了解文件和文件夹的更多信息,例如它们的大小、所有者,以及谁可以对它们进行什么样的处理?为了获取这些信息,需要使用-l选项(或—format=long)。

$ ls -l ~/bin
total 2951
-rw-r--r-- 1 scott scott   15058  2005-10-03 18:49
➥adblock_filters.txt
-rwxr-xr-- 1 scott root       33  2005-04-19 09:45
➥addext

-rwxr--r-- 1 scott scott     245  2005-10-15 22:38
➥backup

drwxr-xr-x 9 scott scott    1080  2005-09-22 14:42
➥bin_on_bacon
-rw-r--r-- 1 scott scott  237641  2005-10-14 13:50
➥calendar.ics

-rwxr-xr-- 1 scott root      190  2005-04-19 09:45
➥convertsize
drwxr-xr-x 2 scott scott      48  2005-04-19 09:45
➥credentials

-l选项代表long,可以看到,它提供了目录中文件的大量数据。我们按从右到左的顺序依次介绍你看到的内容。

最右边一列是最简单的内容:所列文件的名称。需要ls命令显示文件名的更多信息吗?将-F选项加到-l上,如ls-lF。设置颜色也非常容易,就是用ls -lF --color

向左一列,接下来看到的是日期和时间。这是文件最后被修改的时间,包括日期(按照年—月—日的格式)和时间(按照24小时制的格式)。

再向左一列是文件的大小(单位是字节)。对于文件夹来说,它的大小计算有些难捉摸。例如,前面的计数显示bin_on_bacon目录是1 080 B,或者说只比1 KB(kilobyte)多一点,而实际上它里面包含887 KB。根据ls –l命令,credentials目录是48 B,但它里面却什么也没有!怎么回事?

还记得在第1章中,我们介绍目录只是一种特殊的文件,它包含了内部一系列内容吗?在这个例子中,credentials目录除了“..”以外,什么也没有(所有目录都必须引用它们的父目录),所以它仅有48 B。而bin_on_bacon目录包含30多个文件,所以它的大小超过了1080 B。

再往左的两列分别是文件的所有者和所属的组。从前面的列表可以看到,几乎每个文件都属于用户scott和组scott,只有addextconvertsize文件例外,它们属于用户scott和组root

说明 如果需要修改权限,第7章将介绍如何进行操作(提示:用到的命令是chownchgrp)。

再向左,紧接着最后一列,这一列包含了一个数字。如果你正在处理一个文件,这个数字可以告诉你该文件有多少个硬链接(hard link);如果它是目录,这个数字是指该目录中包含有多少项内容。

提示 有关硬链接(和软链接,soft link)的更多信息,可以看看http://www.granneman.com/techinfo/linux/thelinuxenvironment/softandhardlinks.htm,或者在Google中搜索“linux hard links”。

现在轮到最左边的第一列了:每个文件和目录的真正权限。初看起来它们像是某种神秘的代码,不过只要有点知识,其实也非常容易理解。每个代码有10个字符,分成4组(虽然看起来不是这样的)。第一组由第一个字符组成;第二组由第二个到第四个字符组成;第三组包括第五个到第七个字符组成;第四组,即最后一组由第八个到第十个字符组成。例如,可以将credentials目录的权限划分成d|rwx|r-x|r-x

第一组告诉你它是什么类型的文件。前面已经看到-F--color选项用不同的方式来展现这一内容,-l也是如此。d表示credentials是目录,而第一个位置上的则表示该项是一个文件(即便文件是可执行的,ls–l仍然只用一个-来表示,所以-F--color选项可以为你提供更多的信息)。当然在第一个位置还可能看到其他选项,详细内容如表2-3所示。

表2-3 权限字符和文件类型
字  符含  义
-普通文件
-可执行文件
d目录
l符号链接文件
s套接字(socket)
b块设备(block device)
c字符设备(character device)
p命名管道(named pipe)

提示 要查看表2-3中列出的每种设备(至少会有一种)的文件,可以试试ls –l /dev

接下来的9个字符(组成第二组、第三组、以及第四组)分别代表对文件的所有者、文件的组,以及系统中所有其他用户的权限。以在前面显示的addext文件为例,它的权限是rwxr-xr--,这表明所有者scott的权限是rwx,组(在这个例子中还是scott)的权限是r-x,系统中其他用户的权限是r--。这些字符代表什么意思呢?

每个例子中,r表示“允许读取”,w表示“允许改写”(改写意味着修改和删除),x表示“允许执行”,则表示“不允许执行这个操作”。如果原本应该出现r字符的位置,现在出现的是-,则表示“不允许读取”。这一点对于wx也同样适用。

这时再看看addext和它的权限rwxr-xr--,你就会一下子明白过来,所有者scott可以读取、改写和执行文件;组(root)的成员可以读取和执行文件,但是不能改写文件;主机上的其他任何人(通常称为“world”)能够读取文件,但是不能改写文件或者把它作为程序运行。

现在你已经明白了权限的意义,以后会开始注意到某些组合似乎经常出现。例如,“rw-r--r--”权限对于很多文件都很常见,表示所有者既可以读取也可以修改文件,但是组和其他类型的用户就只能读取文件。对于程序文件来说,通常会看到rwxr-xr-x的权限,表示使用计算机的任何人都可以读取和运行程序,但是只有所有者才能修改文件。

然而,目录文件则有些不同了。对于一个文件来说,rwx权限的意义非常明确,即读、写(或修改)或者执行这个文件。但是如何“执行”一个目录呢?

我们先从简单的入手:r。对于目录这种情况,r表示用户可以使用ls命令列出目录的内容。w则表示用户可以在目录中增加更多的文件,重命名已经存在的文件,或者删除不再需要的文件。x相当于为了运行命令而访问目录的权力(需要访问和使用目录中的文件),或者是访问目录中的子目录。

可以看到,-l选项本身的功能非常强大,但是如果和其他选项结合起来它还能发挥更大的作用。你早已学会-a选项的用法,它能显示出目录中的所有文件,那么现在应该非常明白-la的用法(或者用--format=long --all)。

$ la -la ~/
drwxr-xr-x  2 scott scott   200 2005-07-28 01:31  alias

drwx------  2 root root      72 2005-09-16 19:14  .aptitude

-rw-r--r--  1 scott scott  1026 2005-09-25 00:11  .audacity
drwxr-xr-x 10 scott scott   592 2005-10-18 11:22  .Azureus
-rw-------  1 scott scott  8800 2005-10-18 19:55  .bash_history

说明 根据本章前面学到的内容,如果某个文件的所有者和组用户都是scott,就可以从列表中删除这一文件数据。

ls -r

如果你不喜欢-l选项使用的默认字母顺序,则可以使用-r(或者--reverse)选项,以相反的顺序来排列显示内容。

$ ls -lar ~/

-rw-------  8800 2005-10-18 19:55 .bash_history
drwxr-xr-x   592 2005-10-18 11:22 .Azureus
-rw-r--r--  1026 2005-09-25 00:11 .audacity
drwx------    72 2005-09-16 19:14 .aptitude
drwxr-xr-x   200 2005-07-28 01:31 alias

说明 要记住,这里使用的是-r,而不是-R-r表示反向,而-R则表示递归。

当使用-l选项时,输出的结果按照文件和文件夹名称的字母顺序来排序。添加-r选项后,将以相反的顺序输出结果,但仍然是基于文件名排序的。也要记住,在使用ls命令时,如果你想将输入的命令和选项的默认输出结果按相反的顺序排列,可以随时增加这个-r选项。

ls -X

文件名称并不只是字母排序时唯一可用的排序对象。也可以对文件的扩展名进行字母顺序的排序。换句话说,你可以使用ls命令把所有以.doc结尾的文件分为一组,接着是以.jpg结尾的文件,最后是以.txt结尾的文件。使用-X选项(或--sort=extension);如果你想按相反的顺序排序,只需加上-r选项(或者--reverse)。

$ ls -lX ~/src
drwxr-xr-x     320 2005-10-06 22:35 backups
drwxr-xr-x    1336 2005-09-18 15:01 fonts
-rw-r--r-- 2983001 2005-06-20 02:15 install.tar.gz
-rw-r--r-- 6683923 2005-09-24 22:41 DuckDoom.zip

在结果中,最先显示的是文件夹(毕竟,它们没有文件扩展名),后面接着显示具有扩展名的各个文件。尤其是注意install.tar.gz这个文件有两个扩展名,但ls命令在排序时使用最后一个扩展名(.gz)。

ls -t

按字母排序是不错,但是有时还需要按日期和时间对目录的内容进行排序。要想这样做,可以在-l选项的基础上使用-t选项(或者--sort=time);要按相反顺序排序,可以在-l选项的基础上使用-tr选项(或者--sort=time –reverse)。

$ ls -latr ~/
-rw-------   8800 2005-10-18 19:55 .bash_history
drwx------    368 2005-10-18 23:12 .gnupg
drwxr-xr-x   2760 2005-10-18 23:14 bin
drwx------    168 2005-10-19 00:13 .Skype

除了最后一项以外,其他所有项都是在同一天修改的。如果不使用-r选项,结果将按相反的顺序排列,最后一项会出现在第一个位置。

说明 注意在前面这个命令中一次使用了4个选项:-latr。你原本可以用-l -a -t –r来代替上述选项,但是谁愿意输入所有这么多连字符呢?将所有选项组合成一个大选项更快捷,也更容易。这些选项的加长版本(以两个连字符开始,由一个或者两个单词组成的选项)就不能组合在一起,而必须单独输入,如-la --sort=time --reverse

ls -S

除了按照文件名或者扩展名的字母顺序,以及日期和时间对文件排序,还可以使用-S(或--sort=size)选项按照文件大小来排序。

$ ls -laS ~/
-rw-r--r--  109587 2005-10-19 11:53 .xsession-errors
-rw-------   40122 2005-04-20 11:00 .nessusrc
-rwxr--r--   15465 2005-10-12 15:45 .vimrc
-rw-------    8757 2005-10-19 08:43 .bash_history

当按照文件大小排序时,体积最大的文件会排在第一位。要按相反顺序排序,让体积最小的文件排在第一位,只需使用-r选项。

ls -h

在上一节中,.vimrc文件那一行上的15 465表示这个文件大概有15 KB,但是需要自己动脑将字节转换为相应的千字节、兆字节或者吉字节并不总是很方便。通常,使用-h(或--human-readable)选项会更方便,也更容易理解。

$ ls -laSh ~/
-rw-r--r--  100K 2005-10-19 11:44 .xsession-errors
-rw-------   40K 2005-04-20 11:00 .nessusrc
-rwxr--r--   16K 2005-10-12 15:45 .vimrc
-rw-------  8.6K 2005-10-19 08:43 .bash_history

在这个例子中,K代表千字节(kilobyte)。如果文件足够大,还会看到代表兆字节(megabyte)的M,甚至是代表吉字节(gigabyte)的G。或许你会问,使用-h选项时,为什么.nessusrc文件的40 122字节显示为40 KB。记住,1 KB等于1024字节,用40 122除以1 024时,得到的是39.1816406千字节,ls–h就取整为40 KB。1 MB实际上是1 048 576字节,1 GB则是1 073 741 824字节,所以在显示这些值时也会进行类似的取整。

说明 在我的~/.bashrc文件中,定义了以下别名,它们是我用了许多年的。可以使用在本章节学到的内容,扩展这些例子,并创建满足自己需要的别名(有关别名的更多信息,请参见第11章)。

  alias l= 'ls -F'
  alias l1= 'ls -1F'
  alias la= 'ls -aF'
  alias ll= 'ls -laFh'
  alias ls= 'ls -F'

pwd

当然,当在多处列出各个目录的内容时,可能会搞不清楚到底自己处于文件系统的哪个位置上。如何判断自己当前位于哪个工作目录呢?答案就是pwd命令,它代表print working directory(打印工作目录)。

说明 print working directory中的print表示“打印到屏幕上”,并不是“发送到打印机”。

pwd命令显示当前目录(或工作目录)的完整的绝对路径。它并不总是你随时都要用到的命令,但是当你有些困惑时,这个命令非常方便。

$ pwd
/home/scott/music/new

cd

通过指定路径可以列出任何目录的内容,但实际上通常需要切换到一个新目录。这时就要用到cd命令,它是shell迷经常使用的命令。

使用cd命令很简单:只需输入cd,后面跟着你想要切换到的目录。你可以基于当前路径使用相对路径(例如,cd srccd ../../);或者使用绝对路径(例如,cd /tmpcd /home/scott/bin)。

cd ~

你应该知道,cd命令有几个不错的快捷方式。无论在哪儿,只要输入简单的cd,就能立即返回到home目录。这是节约时间的好办法,你将经常用到它。或者,如果你喜欢的话,也可以使用cd ~,因为~就像一个代表“我的home目录”的快捷方式。

$ pwd
/home/scott/music
$ cd ~
$ pwd
/home/scott

cd -

另一个有趣的快捷方式是cd-,可以让你返回到以前的目录,然后再运行pwd命令,显示出新位置(或者说,“原来的”位置)。下面这个例子演示了它是如何运行的。

$ pwd
/home/scott
$ cd music/new
$ pwd
/home/scott/music/new
$ cd -
/home/scott

当你想切换到一个目录,在那里执行操作,然后再切换回原来的目录时,使用cd –命令很有用。由pwd命令在屏幕上提供的其他信息,无异于锦上添花,确保你能知道目录已经切换到你想要的位置。

touch

touch命令并不是你经常使用的命令,但是继续学习本书时需要用到这个命令,所以现在介绍一下这个命令。有趣的是,虽然touch存在的主要目的是更新文件访问和修改时间,但这并不是你现在要使用这个命令的主要原因。相反,这里要用的是它的辅助功能,不过,这个功能确实比它的主要功能还要有用!

说明 只有获得修改文件的权限,才可以对文件使用touch命令,修改它的时间。否则,touch命令会运行失败。

为了同时更新文件(或文件夹)的访问和修改时间,只需要运行基本的touch命令。

$ ls -l ~/
drwxr-xr-x     848 2005-10-19 11:36 src
drwxr-xr-x    1664 2005-10-18 12:07 todo
drwxr-xr-x     632 2005-10-18 12:25 videos
-rw-r--r--     239 2005-09-10 23:12 wireless.log
$ touch wireless.log
$ ls -l ~/
drwxr-xr-x     848 2005-10-19 11:36 src
drwxr-xr-x    1664 2005-10-18 12:07 todo
drwxr-xr-x     632 2005-10-18 12:25 videos
-rw-r--r--     239 2005-10-19 14:00 wireless.log

由于使用了touch命令,wireless.log文件的修改时间和访问时间都修改了,不过ls–l只能显示修改时间。这个文件有一个多月没用过了,但是touch命令现在对它进行了更新,让它看起来好像刚刚使用过(touched)似的。

如果你喜欢,也可以更具体一点儿。如果你只想更新访问时间,就可以使用-a选项(或--time=access)。如果只想更新修改时间,则使用-m(或--time=modify)。

touch -t

请记住,能修改的时间并不只限于当前的日期和时间。相反,只要使用这个选项和一定的模式,就可以修改任何日期和时间:-t [[CC]YY]MMDDhhmm[.ss]。表2-4解释了这个命令中使用的时间模式。

表2-4 用于修改文件时间的时间模式
字  符 含  义
CC 4位年份数字中的前2位字符
YY 2位数字的年份:

• 如果是00~68,则假设年份的前2位数字是20

• 如果是69~99,则假设年份的前2位数字是19

• 如果什么也没有,则假设是当前年份
MM 月份(01~12)
DD 日期(01~31)
hh 小时(01~23)
mm 分钟(00~59)
ss 秒(00~59)

如果你想使用的数字不是普通的两位数,那么在前面补上零是非常重要的,否则时间模式将不起作用。下面是一些带有-t选项的touch命令的例子,通过这些示例来帮助你理解这个命令。

$ ls -l
-rw-r--r--  239 2005-10-19 14:00 wireless.log
$ touch -t 197002160701 wireless.log
$ ls -l
-rw-r--r--  239 1970-02-16 07:01 wireless.log
$ touch -t 9212310000 wireless.log
$ls -l
-rw-r--r--  239 1992-12-31 00:00 wireless.log
$ touch -t 3405170234 wireless.log
$ ls -l
-rw-r--r--  239 2034-05-17 02:34 wireless.log
$ touch -t 10191703 wireless.log
$ls -l
-rw-r--r--  239 2005-10-19 17:03 wireless.log

首先,在前面已经将wireless.log的当前日期和时间建立为2005-10-19 14:00。然后,将时间修改为35年前的某个时间:1970-02- 16 07:01。接着再向前一点,跨越20多年到:1992-12-31 00:00。最后飞跃到未来的时间:2034-05-17 02:34(那时Linux计算机将统治世界,人类生活在和平当中,开源事业非常兴旺),然后结束一切回到我们的当前日期和时间。

从这个例子的演示中,你应该能得出一些经验。通过指定完整的4位数字的年份(1970)、月(02)、日期(16)、小时(07)、以及分钟(01),能回到30多年前的时间。不需要指定秒。之后的操作就再也没有指定4位数字的年份。9212310000中的92在69~99的范围内,所以touch命令假设你需要用“19”作为年份的前两位数字,而3405170234中的34介于00~68,所以touch命令会用“20”作为年份的基础位(前两位)。最后一次使用touch命令时,根本就没有指定年份,只设定了月份(10)、日期(19)、以及小时(17),这时touch命令会明白你想指定的是当前年份(2009)。理解了如何操作touch命令,就可以在需要的时候修改文件的时间戳了。

touch

这一用法不是很多人使用touch命令的主要原因。如果你将touch命令用于不存在的文件时,会产生一个有趣的效果:它将使用你指定的文件名来生成一个空文件。

$ ls -l ~/

drwxr-xr-x   848 2005-10-19 11:36 src
drwxr-xr-x   632 2005-10-18 12:25 videos

$ touch test.txt
$ ls -l ~/

drwxr-xr-x   848 2005-10-19 11:36 src
-rw-r--r--     0 2005-10-19 23:41 test.txt
drwxr-xr-x   632 2005-10-18 12:25 videos

为什么要这样用touch命令呢?你可能想现在先创建一个文件,以后再填充内容。或者你在尝试新发现的命令,需要创建几个文件来执行测试。这些都是使用touch命令的很好理由,当你开始更深入地学习shell时,会发现更多这样的情况。

mkdir

touch命令能够创建空文件,但是如何创建新文件夹呢?使用mkdir命令就能解决这个问题。

$ ls -l

drwxr-xr-x   848 2005-10-19 11:36 src
drwxr-xr-x   632 2005-10-18 12:25 videos

$ mkdir test
$ ls -l

drwxr-xr-x   848 2005-10-19 11:36 src
drwxr-xr-x    48 2005-10-19 23:50 test
drwxr-xr-x   632 2005-10-18 12:25 videos

说明 在大多数系统中,由mkdir命令创建的新目录是赋予所有者读取、改写、以及执行的权限,而组以及其他任何人则只有读取和执行的权限。要想修改这些设置,请参阅第7章介绍的chmod命令。

知道shell还在照看着你,你应该感到高兴: 如果要创建的目录已经存在,命令执行将会失败,并返回一个警告信息。

$ mkdir test
mkdir: cannot create directory 'test' : File exists

mkdir -p

如果在新的子目录的新子目录中再创建一个新的子目录,初看起来这似乎是件相当麻烦的事。创建第一个子目录,cd到其中;然后再创建第二个子目录,再cd到其中;最后创建第三个子目录(唉,真麻烦)。幸运的是,mkdir命令具有一个非常好的选项,使得整个过程更有效率:-p(或--parents)。

$ ls -l

drwxr-xr-x   848 2005-10-19 11:36 src

$ mkdir -p pictures/personal/family
$ ls -l
drwxr-xr-x    72 2005-10-20 00:12 pictures

drwxr-xr-x   848 2005-10-19 11:36 src

$ cd pictures
$ ls -l
drwxr-xr-x    72 2005-10-20 00:12   personal
cd personal
$ ls -l
drwxr-xr-x    48 2005-10-20 00:12   family

mkdir -v

现在不是已经简单多了吗?使用-v选项(或--verbose)甚至会更简单,它能告诉你mkdir命令所执行的每一步操作,这样你就不需要再进行实际检查来确定mkdir命令是否正确完成了任务。

$ mkdir -pv pictures/personal/family
mkdir: created directory 'pictures'
mkdir: created directory 'pictures/personal'
mkdir: created directory 'pictures/personal/family'

成为Linux用户最美妙的事情之一就是,OS想方设法来奖励“懒惰的”用户(越懒越好)。这个选项确实是查看mkdir实际操作的好方法。

cp

复制文件是计算机用户经常做的工作之一,更不用说OS了。Linux shell使用的各命令中,最棒的命令之一就是cp,它用于复制文件和目录。使用cp命令最简单的方法就是输入命令,后面再输入你想复制的文件,然后是被复制文件的新名称。可以把该命令的结构看作是“cp正在从哪复制的文件 正在复制到哪的文件”。这种关系的另一种常见表达方式是“cp 源文件目标文件”。

$ pwd
/home/scott/libby
$ ls
libby.jpg
$ cp libby.jpg libby_bak.jpg
$ ls
libby_bak.jpg libby.jpg

例子非常简单:将图片复制到源文件所在的相同目录中。也可以将文件复制到另一个目录中,甚至可以从非当前工作目录复制到文件系统的其他任何目录。

$ pwd
/home/scott
$ ls ~/libby
libby_bak.jpg  libby.jpg
$ cp pix/libby_arrowrock.jpg libby/arrowrock.jpg
$ ls ~/libby
arrowrock.jpg libby_bak.jpg libby.jpg

这个例子中使用的文件名相同(都是libby_closeup.jpg),没关系,因为是要将这个文件复制到另外一个目录中。但是,在第一个cp命令的例子中,必须使用新的文件名称,用libby_bak.jpg代替libby.jpg,因为这是在将一个文件复制到同一目录。

如果你想从其他目录复制文件到当前工作目录(你目前所在的目录),只需要使用..。(还记得在本章前面学过的.表示“当前目录”吗?现在你就明白它能在这儿派上用场了。)当然,如果使用.就不能更改文件名称,因为它只是原来文件名称的快捷方式。

$ pwd
/home/scott/libby
$ ls
libby_bak.jpg libby.jpg
$ cp pix/libby_arrowrock.jpg .
$ ls
arrowrock.jpg libby_bak.jpg libby.jpg

如果直接将目标文件复制到指定的目录中,则不需要指定目标文件的文件名,只需提供目录名就可以了。

$ ls -l
drwxr-xr-x   224 2005-10-20 12:34 libby
drwxr-xr-x   216 2005-09-29 23:17 music
drwxr-xr-x  1.6K 2005-10-16 12:34 pix
$ ls libby
arrowrock.jpg  libby.jpg
$ cp pix/libby_on_couch.jpg libby
$ ls libby
arrowrock.jpg  libby.jpg  libby_on_couch.jpg

在上个例子中,必须确保名为libby的目录已经存在,以便能将libby_on_couch.jpg复制到其中。否则,最终将在home目录中生成一个名为libby的文件。

cp *

是时候介绍更多的偷懒办法了,也就是用通配符一次将多个文件复制到目录中。精心命名文件,将非常节约时间,因为这样你可以准确地指定一组文件。

$ pwd
/home/scott/libby
$ ls ~/pix
arrowrock.jpg  by_pool_03.jpg on_floor_03.jpg
by_pool_01.jpg on_floor_01.jpg on_floor_04.jpg
by_pool_02.jpg on_floor_02.jpg
$ ls
arrowrock.jpg libby.jpg libby_on_couch.jpg
$ cp ~/pix/by_pool*.jpg .
$ ls
arrowrock.jpg by_pool_02.jpg on_couch.jpg
by_pool_01.jpg by_pool_03.jpg libby.jpg

可以使用的通配符并不只限于“*”。实际上,还可以使用方括号([]),通过匹配“[”和“]”之间的任意字符来更准确地指定你想要复制哪些文件。如果你想复制前三个on_floor图片,但是不需要复制第四个,用带通配符的cp命令就能很容易地实现。

$ pwd
/home/scott/libby
$ ls ~/pix
arrowrock.jpg  by_pool_03.jpg  on_floor_03.jpg
by_pool_01.jpg on_floor_01.jpg on_floor_04.jpg
by_pool_02.jpg on_floor_02.jpg
$ ls
arrowrock.jpg  libby.jpg  libby_on_couch.jpg
$ cp ~/pix/on_floor_0[1-3].jpg .
$ ls
arrowrock.jpg  libby_on_couch.jpg  on_floor_02.jpg
libby.jpg      on_floor_01.jpg     on_floor_03.jpg

cp -v

增加-v选项(或--verbose),则可以在cp命令完成复制任务的同时显示其进度。

$ pwd
/home/scott/libby
$ ls ~/pix
arrowrock.jpg  by_pool_03.jpg  on_floor_03.jpg
by_pool_01.jpg on_floor_01.jpg on_floor_04.jpg
by_pool_02.jpg on_floor_02.jpg
$ ls
arrowrock.jpg  libby.jpg  libby_on_couch.jpg
$ cp -v ~/pix/on_floor_0[1-3].jpg .
'/home/scott/pix/on_floor_01.jpg'
➥-> './on_floor_01.jpg'
'/home/scott/pix/on_floor_02.jpg'
➥->'./on_floor_02.jpg'
'/home/scott/pix/on_floor_03.jpg'
➥->'./on_floor_03.jpg'
$ ls
arrowrock.jpg  libby_on_couch.jpg  on_floor_02.jpg
libby.jpg      on_floor_01.jpg     on_floor_03.jpg

-v选项能够让你对cp命令的执行过程了如指掌。它在这方面做的非常出色,所以实际上你不需要运行最后那个ls命令,因为-v选项能够确保需要的文件事实上已经复制成功了。

cp -i

上一个例子演示了一些有关于cp命令的重要内容,这是你需要了解的。在2.27节的例子中,将3个on_floor图片复制到libby目录中;在上一个示例中,再次将相同的3个图片再次复制到libby目录中。你要复制的目标文件早已存在了,但是cp命令不会发出警告,因为Linux是这样运行的:它假设你知道自己在做什么,所以它不会警告你将要覆盖什么文件,除非你要求它这么做。如果你需要在使用cp命令覆盖文件前得到预警,则可以使用-i选项(或--interactive)。这一次使用-i选项再试着复制相同的文件,将得到不同的结果。

$ pwd
/home/scott/libby
$ ls ~/pix
arrowrock.jpg  by_pool_03.jpg  on_floor_03.jpg
by_pool_01.jpg on_floor_01.jpg on_floor_04.jpg
by_pool_02.jpg on_floor_02.jpg$ ls
arrowrock.jpg  libby_on_couch.jpg  on_floor_02.jpg
libby.jpg      on_floor_01.jpg     on_floor_03.jpg
$ cp -i ~/pix/on_floor_0[1-3].jpg .
cp: overwrite './on_floor_01.jpg'?

砰!cp命令突然停下它的步伐,询问你是否要覆盖它准备开始复制的第一个文件libby_on_floor_01.jpg。如果你想继续并复制这个文件,输入y,否则就输入n。如果你选择输入n,并不意味着cp命令会完全停下来,相反,它会询问你下一个文件如何处置,再下一个文件,直至最后。唯一能够停止整个复制过程的方法就是按Ctrl+c取消命令的执行。同样的,也没有办法提前为所有问题选择yes,所以如果你也打算使用-i选项,将1 000个文件复制为具有相同名称的其他1 000个文件,那么你一定要确保有充足的时间可以坐在电脑前,和你的shell进行交互,因为如果你真的要覆盖文件的话,将被询问1 000次。

警告 对于普通用户来说,通常不需要使用-i选项。但是对于root用户来说,它的责任非常重要。如果root用户错误地覆盖了一个关键的系统文件,则会导致系统出现大问题。因此,在root用户的.bashrc文件中为cp命令创建一个别名是个不错的主意,用cp–i来代替cp

  alias cp= 'cp -i'

cp -R

到目前为止你已经学过了复制文件,但有时还需要复制目录。但是,你不能仅输入“cp 源目录 目标目录”,因为它不会如你预想的那样,虽然目录复制了,但却没有复制里面的文件。

$ pwd
/home/scott
$ cp libby libby_bak
cp: omitting directory 'libby'

如果想要复制目录,还需要包含-R选项(或--recursive),这个选项和ls命令的相应选项差不多,应该能够想得到。增加了-R选项意味着目录和它的内容都会被复制。

$ pwd
/home/scott
$ ls -l
drwxr-xr-x   328 2005-10-17 14:42 documents
drwxr-xr-x   240 2005-10-20 17:16 libby

$ cp -R libby libby_bak
$ ls -l
drwxr-xr-x   328 2005-10-17 14:42 documents
drwxr-xr-x   240 2005-10-20 17:16 libby
drwxr-xr-x   240 2005-10-20 17:17 libby_bak

cp -a

或许你现在正在想cp命令可以用来备份文件,确实如此(虽然还存在更好的程序,例如我们在第15章中介绍的rsync,就是其中一个)。不过只要使用几行bash shell脚本,cp命令也可以作为备份各种文件和目录的有效方法。在这种情况下最有用的选项就是-a选项(或--archive),它相当于以下几个选项的组合:-dpR(或--no-dereference --preserve–recursive)。有关-a选项的另一种说法是:-a选项可以确保cp命令不会复制符号链接的内容(否则可能会极大地增加复制量),只保存关键的文件属性(例如拥有者、时间戳),并递归处理子目录。

$ pwd
/home/scott
$ ls -l
drwxr-xr-x     216 2005-10-21 11:31 libby
drwxr-xr-x     216 2005-09-29 23:17 music
$ ls -lR libby
libby:
total 312
-rw-r--r--   73786 2005-10-20 12:12 arrowrock.jpg
-rw-r--r--   18034 2005-04-19 00:57 libby.jpg
-rw-r--r--  198557 2005-04-19 00:57 on_couch.jpg
drwxr-xr-x     168 2005-10-21 11:31 on_floor

libby/on_floor:
total 764
-rw-r--r--  218849 2005-10-20 16:11 on_floor_01.jpg
-rw-r--r--  200024 2005-10-20 16:11 on_floor_02.jpg
-rw-r--r--  358986 2005-10-20 16:11 on_floor_03.jpg
$ cp -a libby libby_bak
$ ls -l
drwxr-xr-x    216 2005-10-21 11:31 libby
drwxr-xr-x    216 2005-10-21 11:31 libby_bak/
drwxr-xr-x    216 2005-09-29 23:17 music
$ ls -lR libby_bak
libby:
total 312
-rw-r--r--   73786 2005-10-20 12:12 arrowrock.jpg
-rw-r--r--   18034 2005-04-19 00:57 libby.jpg
-rw-r--r--  198557 2005-04-19 00:57 on_couch.jpg
drwxr-xr-x     168 2005-10-21 11:31 on_floor

libby/on_floor:
total 764
-rw-r--r-- 218849 2005-10-20 16:11 on_floor_01.jpg
-rw-r--r-- 200024 2005-10-20 16:11 on_floor_02.jpg
-rw-r--r-- 358986 2005-10-20 16:11 on_floor_03.jpg

说明 是的,可能你已经明白了,但是让我再确定一下:Libby是我的小狗,一只可爱的西施犬,几乎把我写的所有东西最终以某种方式消灭掉。可以在以下链接看到它:http://www.granneman.com/go/libby

mv

cp命令复制文件看起来非常简单,但是移动文件呢?在命令全名的英文单词中去掉多余的相似元音,我们就有了mv命令,是move的简写。

很快你将注意到,前面学到的cp命令的大部分选项与mv命令使用的选项类似。这不足为奇,毕竟mv命令实际上执行了一个cp-a操作,在成功复制文件后再移除原有的文件。

用最简单的话来说,mv命令的作用就是将文件系统的文件从一个地方移动到另一个地方。

$ pwd
/home/scott/libby
$ ls
libby_arrowrock.jpg  libby_bak.jpg  libby.jpg
➥libby_on_couch.jpg  on_floor
$ ls ~/pictures/dogs
libby_on_floor_01.jpg libby_on_floor_03.jpg
libby_on_floor_02.jpg libby_on_floor_04.jpg
$ mv ~/pictures/dogs/libby_on_floor_04.jpg
➥libby_on_floor_04.jpg
$ ls
libby_arrowrock.jpg libby.jpg
➥libby_on_floor_04.jpg
libby_bak.jpg       libby_on_couch.jpg on_floor
$ ls ~/pictures/dogs
libby_on_floor_01.jpg  libby_on_floor_02.jpg
➥libby_on_floor_03.jpg

和使用cp命令一样,如果你不喜欢重复输入文件名称,也可以使用点号“.”来表示当前目录。

$ pwd
/home/scott/libby
$ ls
arrowrock.jpg  libby.jpg  on_couch.jpg  on_floor
$ ls ~/pictures/dogs
on_floor_01.jpg  on_floor_03.jpg
on_floor_02.jpg  on_floor_04.jpg
$ mv ~/pictures/dogs/on_floor_04.jpg .
$ ls
arrowrock.jpg  on_couch.jpg  on_floor_04.jpg
libby.jpg      on_floor
$ ls ~/pictures/dogs
on_floor_01.jpg  on_floor_02.jpg  on_floor_03.jpg

如果你需要将文件移动到另一个目录,并保持文件名相同,则只需要指定目录。目标文件名默认保持不变。

$ pwd
/home/scott/libby
$ ls
arrowrock.jpg  on_couch.jpg  on_floor_04.jpg
libby.jpg      on_floor
$ ls on_floor
on_floor_01.jpg  on_floor_02.jpg  on_floor_03.jpg
$ mv  on_floor_04.jpg on_floor
$ ls
arrowrock.jpg  on_couch.jpg  on_floor_04.jpg
libby.jpg      on_floor
$ ls on_floor
on_floor_01.jpg  on_floor_03.jpg
on_floor_02.jpg  on_floor_04.jpg

为了直观地表达on_floor实际上是一个目录,在文件要移动到的目录名后面写个“/”符号加以标识,是个很好的主意,如mv libby_on_ floor_04.jpg on_floor/。如果on_floor不是目录,mv命令就不会起作用,这样可以防止你无意间覆盖了其他文件。

说明 cpmv命令使用很多相同的选项,这些选项在每个命令中的运行方式也相同。例如,-v可以在复制和移动文件时显示执行过程,-i在复制和移动文件时交互式地提示是否覆盖已经有的文件。

mv

很快将看到,除了移动文件以外,mv还能做更多的事情,这些事情可能看起来有点违背直觉,但是在考虑片刻之后又觉得很有道理。

现在,我们要介绍mv命令另一个很酷的功能。虽然mv命令是用来移动文件的(毕竟,正如其名字所示),但是它也可以用来重命名文件。如果要移动文件,就必须为它指定一个目标名称。没有规则要求目标名称必须与源文件名称相同,所以shell用户从一开始就依靠mv命令来重命名文件和目录。

$ pwd
/home/scott/libby/by_pool
$ ls -F
libby_by_pool_02.jpg  liebermans/
$ mv liebermans/ lieberman_pool/
$ ls -F
libby_by_pool_02.jpg lieberman_pool/

使用cp命令移动目录时,为了复制真实的目录结构,必须指定-R(或--recursive)选项。从前一个例子可以看到,mv命令并非如此,它可以顺利地移动或重命名目录,根本不需要指定任何额外选项,与cp命令相比,这是非常好的改变。

警告 对于mv命令,有个非常重要但又很容易被忽略的细节需要知道。如果你在移动一个指向目录的软链接,就需要特别注意输入的内容。假设在你的home目录中有一个名为dogs的软链接指向/home/scott/pictures/dogs,而你想将这个链接移动到/home/scott/libby子目录中。以下这个命令只移动软链接:

$ mv dogs ~/libby

而以下这条命令移动的是软链接所指向的目录:

$ mv dogs/ ~/libby

区别在什么地方呢?就是在软链接的末端加了一个简单的斜杠(“/”)。没有斜杠的话,移动的是软链接本身,而且只是这个链接;有了斜杠的话,移动的将是软链接指定的目录,而不是软链接本身。一定要小心啊!

rm

rm命令(remove的简写)用来删除文件。这条命令会彻底地删除文件,文件将不复存在。Linux命令行可没有“垃圾箱”或“回收站”之类的东西。你正在走钢丝,一不小心掉下来,就完蛋了。

好吧,这样说有点极端了。shell缺少一个恢复删除文件的命令,这的确是真的。但是如果你错误地删除了文件,也并不意味着完全没有办法。如果你在意识到犯错的瞬间就马上停止使用机器,如果操作系统还没有重写被删除文件所占用的扇区,再加上如果你能够成功地使用某些相当复杂的文件恢复软件,还是有可能恢复误删除的文件的。但是这个过程一点儿乐趣都没有,整个过程都会让你坐如针毡。最好一开始就小心些。

提示 很多人都在尝试为rm命令提供某种安全机制,包括将rm命令重新映射或替换为一个临时的“垃圾箱”(www.comwt.com/open_source/projects/trashcan/);在shell中增加一个“垃圾箱”(www.comwt.com/open_source/projects/trashcan/);创建一个新的命令(如trash)来取代rmwww.ms.unimelb.edu.au/~jrlooker/shell.html#trash)。

另一方面,如果你想确保绝对没有人能恢复你删除的文件,即便是顶级高手也不可以恢复,请使用shred命令来代替rm命令。shred命令会重写文件多达25次,所以再想恢复被删除的文件是不可能的事。但是在使用shred命令之前应该阅读一下man page,因为它的成功率在很大程度上依赖于你正在使用的文件系统的类型。

使用rm命令很简单,甚至有些人可能会认为太容易了。

$ pwd
/home/scott/libby/by_pool/lieberman_pool
$ ls
pool_01.jpg      pool_03.jpg
pool_01.jpg_bak  pool_03.jpg_bak
$ rm pool_01.jpg_bak
$ ls
pool_01.jpg  pool_03.jpg  pool_03.jpg_bak

rm *

使用像“*”这样的通配符,只需要按一下键盘,就能删除多个文件。

$ pwd
/home/scott/libby/by_pool/lieberman_pool
$ ls
pool_01.jpg      pool_03.jpg
pool_01.jpg_bak  pool_03.jpg_bak
$ rm *_bak
$ ls
pool_01.jpg  pool_03.jpg

警告 使用通配符删除文件的时候一定要非常、非常、非常小心,否则有可能删除很多你原本不想删除的文件。一个典型的例子是,应该输入rm *txt而不是rm * txt(看到错误的空格没有?)。这个错误的命令并非只删除所有的文本文件,“*”意味着要删除所有文件,接着再试图删除一个名为txt的文件。很糟糕,不是吗?

rm -v

如果你想知道rm命令执行时到底在做什么,可以使用-v(或--verbose)选项。

$ pwd
/home/scott/libby/by_pool/lieberman_pool
$ ls
pool_01.jpg      pool_03.jpg
pool_01.jpg_bak  pool_03.jpg_bak
$ rm -v *_bak
removed 'pool_01.jpg_bak'
removed 'pool_03.jpg_bak'
$ ls
pool_01.jpg pool_03.jpg

rm -i

-i选项(或--interactive)可以提供一定的安全机制。在删除每个文件之前,它会先询问你是否要删除。当你作为root用户运行系统的时候,这可是个好东西!

$ pwd
/home/scott/libby/by_pool/lieberman_pool
$ ls
pool_01.jpg      pool_03.jpg
pool_01.jpg_bak  pool_03.jpg_bak
$ rm -i *_bak
rm: remove regular file 'pool_01.jpg_bak'?  y
rm: remove regular file 'pool_03.jpg_bak'?  y
$ ls
pool_01.jpg   pool_03.jpg

rm命令询问你要如何做的时候,y表示同意删除文件,n表示忽略这个文件,并继续处理下一个文件。

rmdir

删除文件一点也不难,但是删除目录呢?

$ pwd
/home/scott/libby/by_pool
$ ls
pool_02.jpg  lieberman_pool  lieberman_pool_bak
$ ls lieberman_pool_bak
pool_01.jpg      pool_03.jpg
pool_01.jpg_bak  pool_03.jpg_bak
$ rm lieberman_pool_bak
rm: cannot remove 'lieberman_pool_bak/':  Is a directory

花点时间找找,你可能会发现rmdir命令,它就是专门用来删除目录的。那么快来试试吧。

$ rmdir lieberman_pool_bak
rmdir: 'lieberman_pool_bak/': Directory not empty

怎么会这样呢?根本不能用嘛。rmdir命令只能删除空目录。在这个例子中,lieberman_pool_bak文件夹只包含了4项内容,所以清空这个目录不算是很难的事,之后就可以使用rmdir命令了。但是如果想要删除的目录包含了10个子目录,每个子目录中又包含10多个子目录,每个单独的子目录中还包含25个文件,该怎么办呢?你打算不停地删除文件吗?还有一种更简单的办法!请看下一节介绍的命令。

rm -Rf

删除非空目录,还有一种更为简单的方法:把-R(或--recursive)和-f(或--force)选项结合起来使用。-r选项告诉rm命令进入到它发现的每个子目录中去删除文件,而-f选项只是告诉rm命令完成任务但不要因为些细节问题(比如文件夹非空)而烦扰用户。

$ pwd
/home/scott/libby/by_pool
$ ls
pool_02.jpg  lieberman_pool  lieberman_pool_bak
$ ls lieberman_pool_bak
pool_01.jpg      pool_03.jpg
pool_01.jpg_bak  pool_03.jpg_bak
$ rm -Rf lieberman_pool_bak
$ ls
pool_02.jpg lieberman_pool

噢!它是删除目录及其中所有文件和子目录的一种稳妥的办法。

警告 rm–Rf命令有可能损坏你的重要文件和系统。

经典的Linux警告就是,当作为root登录时,不要输入rm -Rf /*命令。这样你将删除你的系统。你会觉得很糟糕,觉得这样做很傻。

一般来说,在rm–Rf命令中使用通配符要小心谨慎。rm -Rf libby*rm -Rf libby *之间有着巨大的差异。前者会删除工作目录中以libby开头的所有东西,后者会删除任何名称恰好是libby的文件或文件夹,然后再删除目录中的所有其他东西。

也可能在无意间你就闯下了大祸,原本你想输入rm -Rf ~/libby/*,但是手指一不留神就输错了命令,告诉shell执行rm -Rf ~/libby /*。首先是~/libby目录被删除了,然后你的文件系统被迅速删除。

有一条很棒的小建议:永远不要输入rm -Rf .*/*删除以点号.作为名称开始的目录,因为这也匹配..,最终将删除当前工作目录的上级目录的所有东西。哎呀,糟了!

再重申一次:作为普通用户,一定要慎用rm –Rf命令。作为root用户使用rm–Rf命令时一定要保持高度警惕。

在结束rm命令的讨论之前,你应该了解一些这个命令与系统中特定文件之间的关系。首先,不管怎么删除,都不可能删除目录...,因为它们是保持文件系统层次结构所必需的。况且,为什么非要删除它们呢?就让它们呆在那儿吧。

如何删除名称中带有空格的文件呢?调用rm命令的普通方法(命令名,后面跟着文件名),是不会起作用的,因为rm命令认为你说的是两个不同的文件。实际上,删除Cousin Harold的图片并不太难。只需要将文件的名称用引号括起来就可以了。

$ ls
cousin harold.jpg -cousin_roy.jpg cousin_beth.jpg
$ rm cousin harold.jpg
rm: cannot remove 'cousin': No such file or  directory
rm: cannot remove 'harold.jpg': No such file or  directory
$ rm "cousin harold.jpg"
$ ls
-cousin_roy.jpg cousin_beth.jpg

下面是一个更让人头疼的问题:如何删除名称由“-”字符开始的文件呢?

$ ls
-cousin_roy.jpg  cousin_beth.jpg
$ rm -cousin_roy.jpg
rm: invalid option -- c
Try 'rm --help' for more information.

噢!rm命令看到“-”符号时,会认为它是一个选项的开始,但是它并不认识由字母c开始的选项。接着再遇到的是ousin_roy.jpg,它更不知道如何处理了。

有两种解决方案。你可以在有问题的文件名称的前面放 “--”,它会告诉命令:跟在后面的任何东西都不能作为选项,而是文件或文件夹的名称。

$ ls
-cousin_roy.jpg cousin_beth.jpg
$ rm -- -cousin_roy.jpg
$ ls
cousin_beth.jpg

除此以外,还可以使用.作为路径名的一部分,这样,就可以在-干扰rm命令,让它以为文件名是实际选项之前就避免这个问题。

$ ls
-cousin_roy.jpg cousin_beth.jpg
$ rm ./-cousin_roy.jpg
$ ls
cousin_beth.jpg

足智多谋的Linux用户总能深入思考想解决问题的办法。还有,不要在文件名开始的地方用连字符。

su username

su命令代表“switch user”(切换用户),并不是大多数人想象的“super user”(超级用户),能够让某个用户暂时以另一个用户的身份进行操作。当在shell中想快速成为root用户时,这个命令最常用,运行一两个命令,然后恢复到普通的非root用户。可以把它想象成Clark Kent变成他的超人形象的情形,纠正一些错误后,又变成普通人的样子。

调用su命令并不难。只要输入su,后面跟上你想要采用的用户身份就可以了。

$ ls
/home/scott/libby
$ whoami
scott
$ su gromit
Password:
$ whoami
gromit
$ ls
/home/scott/libby

例子中用了一个新命令,不过,实际上并没有广范使用,它就是whoami。它只是告诉你你是谁(就shell关心的用户来说)。这里用它来检验su命令是否按你所期待的那样工作。

su -l

只有在知道用户密码的情况下,su命令才起作用。没有用户密码,就不能切换用户。如果正常运行,你就会切换到用户在/etc/passwd文件中指定的shell,如shtcsh或者bash。大多数Linux用户只使用默认的bash shell,所以你可能不会看到有任何区别。在上一个例子中也要注意的是,当改变用户时目录并没有发生变化。从本质上来说,你已经变成了gromit,但使用的仍然是scott的环境变量。这好比是你发现了超人的衣服并穿上了它,看起来你像个超人了,但是还没有拥有超人的任何能力!

解决这个问题的办法就是使用-l(或--login)选项。

$ ls
/home/scott/libby
$ whoami
scott
$ su -l gromit
Password:
$ whoami
gromit
$ ls
/home/gromit

这个例子看起来与2.41节中的例子大概相同,但是本质内容区别很大。事实是你现在位于gromit的home目录,这就证明确实发生了变化。-l选项告诉su命令使用一个登录shell,好像gromit真正登录到机器中一样。现在你用的是gromit的名字,使用的也是gromit的环境变量,还位于gromit的home目录(就是gromit初次登录到这台机器后所在的位置)。这次就好像既穿上了超人的衣服,又拥有了超人一跃冲天的超能力。

su

在本章开始曾多次提及用su命令来将其他用户身份变更成root。你可以使用su root,或者更好一些,使用su -l root,但是还有一种更快捷的方法。

$ whoami
scott
$ su
Password:
$ whoami
root

su -

只输入su相当于输入了su root,即拥有了root的名义和能力,而且只有这些了。实际上,非root环境变量仍然存在,如下所示:

$ ls
/home/scott/libby
$ whoami
scott
$ su
Password:
$ whoami
root
$ ls
/home/scott/libby

当使用su –时,不但用户变更成了root,而且还使用了root的环境变量。

$ ls
/home/scott/libby
$ whoami
scott
$ su -
Password:
$ whoami
root
$ ls
/root

现在就好多了!在su后面附加“-”与su -l root的效果相同,但是输入的字符少一些。拥有了root的用户名、能力和环境,意味着你完全成为root用户了。对于计算机来说,root用户能够执行的任何操作,你也可以做。好好享受一下你的超能力吧,但是要记住,巨大的能力也会带来巨大的潜在危险,你知道这样的结果将是什么。

如果这是在法律学校,本章应该让你明白了什么是轻罪、侵权和重罪。如果这是在硬件维修班,那你已经知道了什么是内存、硬盘和主板。但这是一本讲Linux shell的书,而你已经学习了Linux用户为了有效使用命令行而需要知道的基本命令:lspwdcdtouchmkdircpmvrmsurmdirwhoami则是意外的收获。在掌握了这些极其重要的命令以后,接下来就可以着手学习命令了。这话听起来有点绕,不是吗?学完下一章你就会明白了。



第3章 学习命令

第2章已经讲述了一些基本的系统命令。虽然涉及的东西不少,但还有很多内容没讲。ls命令是一个选项极为丰富、功能非常强大的工具,而且其选项远远不止第2章提到的那些。那么怎样才能学到关于命令的更多知识或其他能引起你兴趣的内容呢?甚至在连名字也不知道的情况下,如何发现相应的命令呢?这些就是本章要讲述的内容。对于已经知道的命令,你会学到怎么得到更多的信息,还会学到一些你不太了解的命令和一些你完全不知道的命令。

我们首先介绍两个非常重要的命令:maninfo,然后再延伸到一些更小、更准确的命令——也是实际上使用由man收集的数据的命令。学习完这些命令,就可以着手学习shell环境中的各种工具了。

man ls

想了解Linux命令的用法吗?当然,这很容易!假设你想查ls命令的更多信息,输入man ls,就会打开man page(man是manual的缩写,因此man page就是“手册页面”),显示关于ls命令各个方面的信息。试着查一查本书中已经介绍的其他命令的更多信息,你会发现(几乎)所有的命令都有各自的man page。

虽然man page非常有用,但是它们仍然存在一些问题。你必须知道命令的名称才能使用它们(虽然有各种方法能够解决这个特殊问题),但有时候它们显示的信息要么是过时的,要么就是遗漏了对命令的最新特性的介绍。并不是每个命令都有man page,这个问题也很烦人。但是最糟糕的情况是,对于你感兴趣的命令,即使找到了它的man page,而且也是最新的,仍然可能会有个大问题:它可能没有包含你想找的信息。

通常,man page是由编写程序的开发人员编写的(但也有例外)。编写Linux分发版中应用程序的开发人员大多是优秀的程序员,但对于自己的劳动成果,他们却不是总能很清楚明确地写出来或讲解出来。他们知道程序是怎么运行的,但是他们常常忘记用户并不了解那些开发人员司空见惯的技术细节。

不过,即使有这么多问题,man page仍不失为供各层次Linux用户学习的好资源。如果想在命令行中使用Linux,就需要学习如何使用和阅读man page。

如前所述,使用这个命令并不难,只需要输入man,后面再跟上你想要了解的命令。

$ man ls
LS(1)            User Commands         LS(1)
NAME
  ls - list directory contents
SYNOPSIS
  ls [OPTION]... [FILE]...
DESCRIPTION
  List  information  about  the FILEs (the current
  directory by default).
  Sort entries alphabetically if none of -cftuSUX
  nor --sort.
  Mandatory arguments to long options are mandatory
  for short options too.
  -a, --all
    do not hide entries starting with .
  -A, --almost-all
    do not list implied . and ..
[Listing condensed due to length]

在这个例子中,man命令提供的资料非常详细——实际上超过了200行。当然,并不是所有命令都提供了这么多信息,不过有些命令则提供得更多。你的工作就是阅读man page提供的各个部分,它通常(但并不总是)由以下几部分组成。

  • NAME(命令名称)——命令的名称和简要的介绍。
  • SYNOPSIS——命令的基本格式。
  • DESCRIPTION——描述命令功能的概要介绍。
  • OPTIONS(选项)——man命令最基本的部分:命令的各种选项,以及对每个选项的简短介绍。
  • FILES(文件)——命令使用的其他文件。
  • AUTHOR(作者)——编写命令的作者,以及联系信息。
  • BUGS(错误)——已知的错误,以及如何报告新错误。
  • COPYRIGHT(版权声明)——它的意义很明显,即版权信息。
  • SEE ALSO(参见)——其他相关的命令。

在man page中来回翻看并不是很难的事情。一次向下移动一行,使用向下的箭头键;一次向上行一行,使用向上的箭头键。向下翻动一页,按空格键或者f [代表“forward(向前)”];向上翻动一页,按b键[代表“backward(向后)”]。当到达man page的底部时,man命令可能会自己退出,回到shell中;但是有时候可能只是停在底部,并没有退出,在这种情况下可以按q键退出程序。事实上,如果没有找到想要的信息,随时都可以按q键退出man命令。

在man page中查找某一特定项是很难的,所以有时候需要进行一些搜索。打开man page以后要搜索其内容,先输入“/”,后面跟上搜索的词语,然后按Enter(回车)键。如果搜索的词语存在,页面就会跳转到相应的位置;如果要跳转到下一个搜索结果,再按一次Enter键(或者n键),然后一直按Enter键(或者n键)以便在屏幕上查看找到的每个搜索信息;按Shift+n,返回。

man -k

稍微练习一下,你就能够在man page中自由移动,并找到真正需要的东西(假设你知道应该读取哪个man page)。假设你对命令能完成的功能有点了解,但不知道命令的实际名称,该怎么办呢?这时可以试试-k选项(或--apropos),搜索一个描述你想找的那种命令的词或短语。搜索的结果是一个命令列表,列出了其名称或简单说明能够匹配搜索词的所有命令。

$ man list
No manual entry for list
$ man -k list
last (1) - show listing of last logged in users
ls (1) - list directory contents
lshal (1) - List devices and their properties
lshw (1) - list hardware
lsof (8) - list open files
[Listing condensed due to length]

使用-k选项要谨慎,因为它可能会生成长长的一串结果,这样反而让你找不到想要查找的内容。尝试使用不同的搜索词语时,不要担心。如果你觉得这可能有助于找到需要的命令的话,尽管去试试。

提示 -k选项(也可以表示为--apropos),与apropos命令的功能完全相同。

man -f

如果你知道一个命令的名称,但是不知道它的功能,有种简单快捷的方法可以查找到它的功能,而无需打开命令的man page。使用-f选项(或--whatis),就可以看到这个命令的简单说明。

$ man -f ls
ls (1)             - list directory contents

提示 -f选项(也可以表示为--whatis)与whatis命令非常相像,本章稍后会详细介绍whatis

man -u

当使用man命令查找一个命令的信息时,man命令偶尔可能会报告并没有该命令的页面。在放弃查找之前,还可以再试试-u选项(或--update),这个选项强制man命令重建它使用的命令数据库和man page。如果你觉得man命令的查找结果不应该是看起来的那个样子,通常用这个选项作为首选的尝试办法。

$ man ls
No manual entry for ls
$ man -u ls
LS(1)            User Commands           LS(1)
NAME
  ls - list directory contents
SYNOPSIS
  ls [OPTION]... [FILE]...
[Listing condensed due to length]

man [1-8]

你可能注意到,在前面列出的ls命令的man page的第一行引用了LS(1);再往前,当使用-k选项时,所有命令的名称后面都跟一个用括号括起来的数字。其中大部分数字是1,但是lsof命令后的数字是8。为什么会出现这些数字呢?

答案在于man page按照其类型分成了不同的区段(section),分别标记为数字1到8,划分的各区段如下所示(如果你不理解例子中的某些区段,不必担心,因为很多区段本来就相当神秘和特殊)。

  1. 普通命令,如cdchmodlpmkdirpasswd

  2. 由内核提供的底层系统调用,如introchmod

  3. C库函数,如beepHTML::ParserMail::Internet

  4. 特殊文件,如/dev中找到的设备,包括控制台(console)、打印机(lp)和鼠标(mouse)。

  5. 文件格式和约定,如apt.confdpkg.cfghostspasswd

  6. 游戏,如atlantikbouncingcowkmahjonggrubik

  7. 杂项,包括宏包(macro package)。如asciisambautf-8

  8. root用户使用的系统管理命令,如mountshutdown

到目前为止,本书中出现的命令几乎都属于第1区段,这并不让人觉得奇怪,因为我们关注的是Linux系统的普通应用。但是注意,有些命令会同时属于多个区段:例如,chmod同时属于第1区段和第2区段,而passwd则同时属于第1区段和第5区段。在默认情况下,如果在shell中输入man passwdman命令默认打开序号较小的区段页面,所以你将得到passwd的第1区段的man page。如果你想学习passwd文件的更多内容,第1区段的内容不一定很有帮助。要查看passwd文件的man page,需要为man命令提供你想查看的数据所属于的区段序号。

$ man passwd
PASSWD(1)                            PASSWD(1)
NAME
  passwd - change user password
SYNOPSIS
  passwd [-f|-s] [name]
  passwd [-g] [-r|-R] group
  passwd [-x max] [-n min] [-w warn] [-i inact]
login
  passwd {-l|-u|-d|-S|-e} login
DESCRIPTION
  passwd changes passwords for user and group 
➥accounts. A normal user...
[Listing condensed due to length]
$ man 5 passwd
PASSWD(5)                            PASSWD(5)
NAME
  passwd - The password file
DESCRIPTION
  passwd contains various pieces of information for 
➥each user account.
[Listing condensed due to length]

man -t

打印man page和用终端程序查看man page一样简单,有时为了方便阅读和思考,需要打印出man page。然而打印man page并不可以一步到位,打印特定区段的命令要用到稍后会详细讲述的原则。但是如果你想打印man page,这种方法可以做到。目前只需要先有这么个概念,阅读后面的章节后你将会更加理解这些命令的含义。

假设有一台打印机已经连接到系统中,并将它标记为hp_laserjet。要将ls命令的man page直接用这个打印机打印,就需要使用-t选项(或--troff),再通过管道(pipe)输出至lpr命令,并在lpr命令中用-P选项来标识相应的打印机。

$ man -t ls | lpr -P hp_laserjet

说明 第4章将介绍管道符(|)的用法,第6章将介绍lpr命令的用法。

稍等一会儿(时间长短视计算机和打印机的速度而定),打印机hp_laserjet就开始打印ls命令的man page了。不过,或许你并不真想打印man page页面,可能为ls命令的man page创建PDF就足够了。再重申一次,这个命令现在所做的事情或许看起来很神秘,但是很快就可以真相大白的。

再次使用-t选项,但这次是将输出结果发送到一个根据ls命令命名的PostScript文件。如果处理过程成功完成,就可以使用ps2pdf命令将PostScript文件转换为PDF文件。转换正常完成后,可以删除掉原来的PostScript文件,因为不再需要它了。

$ man -t ls > ls.ps && ps2pdf ls.ps && rm ls.ps

说明 第4章将学习“>”和“&&”符号的用法,第6章将学习ps2pdf命令。

如果要为自己喜欢用的命令创建一个打印好的man page库,或者将这个库制作成PDF格式(必要时再打印出来),现在你应该知道如何去做了。事情就这么简单,man命令如此强大和灵活,它的功能甚至比现在看到的这些还要强大。

info

man命令和生成的man page,用起来都很简单,只是它的内容可能没有你希望的那么用户友好。为了解决这一问题,以及在使用man page时可以感觉到的其他不足之处,GNU Project(它在各个方面负责维护本书中介绍的许多命令)创建了它自己的格式:Info页面,它使用Info命令查看。

就内容来说,Info页面比man page编写得要更好、更容易理解,也更友好,但man page使用起来确实要更容易得多。一个man page只有一页,而Info页面几乎总是将它们的内容组织成多个区段(称为节点),每个区段也可能包含子区段(称为子节点)。理解这个命令的窍门就是不仅要学习如何在单独的Info页面中浏览导航,还要学习如何在节点和子节点之间切换。可能刚开始会一时很难在Info页面的节点之间移动和找到你要的东西,真是具有讽刺意味:原本以为对于新手来说,某个东西比man命令会更好些,但实际上学习和使用起来更困难。

info命令有很多用法,学习使用info最好的命令,事实上就是info。为了学习如何使用info命令,以及如何阅读Info页面,只需输入以下命令:

$ info info

这就打开了info命令的Info页面。现在你需要学习如何在这个新的Info世界中畅游了。

在特定区段的屏幕中要向下或向前一次移动一行,应该使用向下的箭头;如果要向上或向后一次移动一行,则应该使用向上的箭头。当到达特定区段的最底部或结尾时,光标就会停在那里,不能继续移动了。

如果想一次向下移动一个页面,可以使用键盘的PageDown键;一次向上移动一个页面,则使用PageUp键。但是你不能离开所在的特定区段。

如果到达区段的底部时想跳回到顶端,只需要按b(代表beginning)就能够回到开始的位置。同样,按e(代表end)则是跳回到底部。

在页面上跳转时,如果发现某些奇怪的事情,例如字母或者单词有点扭曲,可以随时按Ctrl+l键重回屏幕,所有一切就会恢复正常了。

现在你已经知道了如何在一个特定的区段或节点中导航,接下来我们学习如何在节点之间导航。如果你不想使用PageDown和PageUp键在一个区段内前进或后退,则可以使用空格键向下翻页,用Backspace或者Delete键向上翻页。这些键比PageDown和PageUp键好用,除了更容易触及以外,还有一个好处就是:当到达节点的终点时,会自动继续移动到下一个节点(如果存在子节点的话,也会通过子节点)。同样,向上移动也会回到上一个节点,并通过任何子节点。使用空格键、Backspace键或Delete键,你就能够快速地在关于某个特定命令的一套完整的Info页面之间随意移动。

如果想减少按键的次数,可以输入n(表示next),移动到同一级的下一个节点。如果你正在阅读的节点包含子节点,输入n时,就会忽略那些子节点,直接移动到与当前阅读节点处于同一级别的下一节点。不过,如果正在阅读子节点时输入n,将跳到下一个子节点。如果输入n会让你移动到当前级别的下一个节点,那么输入p(p表示previous)则可以让你回到同一级别的前一个节点。

如果你想前进到一个节点或者子节点,则可以使用](右方括号)键。如果在阅读节点的时候按]键,将跳到这个节点的第一个子节点(如果存在子节点的话)。否则,将移动到与这个节点处于同一级的下一个节点。同样,要向后移动,则可以使用[(左方括号)键。

如果想上移一个节点,回到当前正在阅读节点的父节点,则可以使用u(表示up)键。不过要小心,这样很容易跳过你正在Info中阅读的命令的主页面,到达称为Directory的节点,即通往所有其他Info节点的根节点[另一种访问Directory节点的办法是随时输入d(表示 directory)]。

Directory节点是这种页面的典型示例,你可以通过它找到所有的Info页: 其实就是个Menu(菜单)页,列出了所有的节点或子节点。如果你发现自己位于Menu页面中,可以使用以下两种方法快速导航到菜单中列出的各子节点。第一种方法是,输入一个m(代表menu),然后再开始输入你想要跳转到的子节点的名字。例如,以下是在命令行中输入info info时看到的第一个页面:

File: info.info,  Node: Top,  Next: Getting Started, Up: (dir)

Info: An Introduction
*********************
The GNU Project distributes most of its on-line
manuals in the "Info format", which you read using
an "Info reader". You are probably using an Info
reader to read this now.
[content condensed due to length]

* Menu:
* Getting Started:: Getting started using an Info reader.
* Expert Info:: Info commands for experts.
* Creating an Info File:: How to make your own Info file.
* Index:: An index of topics, commands, and variables.

要跳到Expert Info页,可以先输入m,然后再跟着输入Exp。这时,你可以继续完成输入ert Info,或者也可以只按一下Tab键,Info命令就会用能够匹配已经输入字符的菜单项的名称来补充你的输入。如果Info命令提示输入有误,这时或许是你的输入本身有问题,或许是有多个菜单项可以匹配你已经输入的内容。修复你的输入错误,或者输入更多的字符,使Info命令能够很明显地判断出哪个菜单项是你感兴趣的内容。如果这时你觉得不需要进行Menu选择了,则可以按Ctrl+g键取消命令,回去阅读你原来感兴趣的节点。

另一种方法是,用上下箭头键将光标移动到你需要的菜单项,然后按Enter键。这两种方法都可以。

如果你不想浏览Info页面,想进行搜索,也可以按以下两种方法进行:搜索关于特定命令的Info页面中所有节点的标题,或者搜索和特定命令有关的所有节点的实际文本。如果要搜索标题,则输入i(表示index,因为这种搜索方法使用的是由Info命令创建的节点索引),后面跟着你要搜索的内容,然后按Enter键。如果搜索内容在某个节点标题中存在,就会跳到那个节点。如果想继续搜索,查找下一个结果,按逗号键。

如果想搜索的不是标题,而是文本,则输入s(表示search),后面跟着要搜索的词或短语,然后按Enter键。要重复搜索,按s键,再立即按Enter键。这一操作不像搜索标题时只需按逗号键那么容易,但还是管用的。

如果在Info中迷失方向,需要帮助,只需要随时按“?”键,窗口的下半部分就会显示Info的各种选项。可以使用前面学过的键盘操作来上下移动。要退出帮助,按l。

最后,或许也是最重要的一点,要完全退出Info,只需要按q(表示quit),就能够返回到shell中。

whereis

whereis命令的功能非常有用:它可以告诉你命令的可执行程序、它的源文件(如果存在的话)及其man page的路径。例如,以下命令会得到Kword的信息,它是KOffice程序套件中的一个字处理器(假设已经安装了二进制文件、源文件和man文件)。

$ whereis kword
kword: /usr/src/koffice-1.4.1/kword /usr/bin/kword
➥/usr/bin/X11/kword usr/share/man/man1/kword.1.gz

whereis命令首先报告源文件的位置/usr/src/koffice-1.4.1/kword。接着报告任何可二进制可执行文件的位置/usr/bin/kword/usr/bin/X11/kword。在这台计算机的两个位置都能找到KWord,虽然有些不寻常但还不算奇怪。最后找到的是man page的位置/usr/share/man/man1/kword.1.gz。有了这些信息后,你现在就能够确认这台计算机上确实已经安装了这个程序,而且也能够知道怎么运行它。

如果只想搜索二进制文件,可以使用-b选项,如下所示:

$ whereis -b kword
kword: /usr/bin/kword /usr/bin/X11/kword

如果只想搜索man page,使用-m选项,如下所示:

$ whereis -m kword
kword: /usr/share/man/man1/kword.1.gz

最后,如果只想搜索源文件,可以尝试-s选项,如下所示:

$ whereis -s kword
kword: /usr/src/koffice-1.4.1/kword

whereis命令是一种非常好用的快捷方法,可以找到你正在使用的计算机中程序的重要信息。你会发现它的用处超乎你的想象。

whatis

本章前面介绍过man命令的-f选项,它可以将在man page中找到的命令描述打印到屏幕上。如果你还记得man –f能够提供这些信息,你真的很棒!不过,要记住whatis命令则要容易得多,它也能做同样的事:显示命令的man page的描述。

$ man -f ls
ls (1) - list directory contents
$ whatis ls
ls (1) - list directory contents

whatis命令还支持正则表达式和通配符。要用通配符搜索man数据库,可以使用-w选项(或--wildcard)。

$ whatis -w ls*
ls (1) - list directory contents
lsb (8) - Linux Standard Base support for Debian
lshal (1) - List devices and their properties
lshw (1) - list hardware
lskat (6) - Lieutnant Skat card game for KDE
[Listing condensed due to length]

与不使用选项的whatis命令相比,使用通配符可能会让搜索速度稍微慢一些,但是对于今天运行速度越来越快的计算机来说,这种影响是微不足道的,所以大可不必为此担忧。

正则表达式可以和-r(或--regex)选项一起使用。

$ whatis -r ^rm.*
rm (1) - remove files or directories
rmail (8) - handle remote mail received via uucp
rmdir (1) - remove empty directories
rmt (8) - remote magtape protocol module

提示 本书没有详细介绍正则表达式,但是可以阅读由Ben Forta撰写的Sams Teach Yourself Regular Expressions in 10 Minutes(ISBN: 0672325667)来获取更多信息。

虽然使用正则表达式可能会让whatis命令的响应速度变慢,但是你可能根本不会注意到这个问题。

whatis命令便于记忆(至少要比man –f更容易),而且它还能快速返回一些重要信息,因此一定要记住它。

apropos

whatis命令与man –f类似,而apropos命令则同样与man –k类似。这两个命令都能够在man page中搜索命令的名称和描述,当你只记得命令的功能而不记得具体名称时,就可以为你提供帮助。

说明 在这里这样用apropos,确实不符合通常的用法,但它的确是一个词,基本上就是“相关的”或者“恰当的”意思。单词appropriate也有类似的意思,但是它的拉丁语词根与apropos并不相同。如果你是个语言爱好者,可以到www.answers.com查查这些词。

apropos命令的使用很简单:只需要在命令后面输入一个单词或短语,描述你感兴趣的命令的功能就可以了。

$ man list
No manual entry for list
$ man -k list
last (1) - show listing of last logged in users
ls (1) - list directory contents
lshw (1) - list hardware
lsof (8) - list open files
[Listing condensed due to length]
$ apropos list
last (1) - show listing of last logged in users
ls (1) - list directory contents
lshw (1) - list hardware
lsof (8) - list open files
[Listing condensed due to length]

whatis命令一样,可以使用-w(或--wildcard)和-r(或--regex)选项进行搜索。不过更有趣的是,如果只想密切关注某个词或者短语,没有任何其他内容,则可以使用-e选项(或--exact)。例如,在前面的结果列表中,搜索list的结果中却出现了last命令,因为它的描述中含有listing这个词。现在我们用-e选项进行相同的搜索。

$ apropos -e list
ls (1) - list directory contents
lshw (1) - list hardware
lsof (8) - list open files
[Listing condensed due to length]

这次,last命令就没有出现了,因为你只想让结果精确地匹配list这个词,而不是listing。事实上,搜索list的结果由不使用-e选项时的80个缩减为使用-e选项时的55个,这样就方便你准确地查找命令搜索结果,找到你真正想要的命令。

which

回想一下whereis命令,以及用-b选项只查询KWord的二进制文件信息时发生的情况。

$ whereis -b kword
kword: /usr/bin/kword /usr/bin/X11/kword

在两个位置都有KWord的可执行文件。但是哪一个文件会先运行呢?运行which命令就可以得到答案。

$ which kword
/usr/bin/kword

只要输入命令的名称,which命令就能告诉你这个命令的哪个版本将会运行。换句话说,如果你输入kword,然后按Enter键,shell将会执行/usr/bin目录下找到的kword程序。如果想运行/usr/bin/X11目录下的版本,必须用cd命令切换目录,然后输入./kword,或者是使用那个命令的绝对路径,输入/usr/bin/X11/kword

which命令也是一种快速判断系统中是否存在特定命令的方法。如果在你的系统中有相应的命令,而且也在PATH中,which命令将告诉你在哪可以找到这个命令。如果命令不存在,将不在命令行中返回任何信息。

$ which arglebargle
$

如果要查找命令的所有位置(就像使用whereis –b一样),则可以使用-a(表示all)选项。

$ which -a kword
/usr/bin/kword
/usr/bin/X11/kword

本章讲述的内容就是“学习命令行”。你已经看到在命令行中可以通过很多方法来查找相关选项的更多信息。两个得力助手就是maninfo命令,它们为Linux计算机中能找到的几乎所有命令提供了大量的数据和描述信息。也要记住whereiswhatisaproposwhich命令也都有各自可以完成的功能,尤其如果你的目标是不想去查看maninfo提供的那些有时让人眼花缭乱的大段信息,这些简洁的命令就能派上用场。不过,虽然这个目标可以理解,但经常不能实现。有的时候不得不挽起袖子、鼓起勇气去阅读man page。可以把它想象成西兰花(broccoli),也许你并不喜欢它的味道,但是它对身体确实有益。

本章介绍的很多命令的功能在一定程度上确实有些重复。例如,man –kapropos相同,man –fwhatis相同,whereis –bwhich –a相同。在给定的情况下具体选用哪个命令完全由你自己决定。但是了解各种功能类似的命令仍然是有好处的,这样你就能够看懂其他人写的shell脚本或指令,准确地理解要执行的操作。Linux世界充满了变化和选择,甚至在通过shell运行命令这样的小事情上也没有例外。



第4章 组合命令

我们小的时候,首先学会了数字,后来又学会了如何用加减乘除之类的数学符号把数字组合起来。到目前为止,你已经从本书中学到了很多命令,但一次只能运行一个命令。实际上,使用不同的符号(例如|>>、和<),可以把这些命令组合起来实现更复杂、更有趣的操作。本章就介绍这些功能更强大的组合命令,其中既包含了此前已学到的命令,也使用了一些后续章节中还会详细介绍的命令。

;

如果需要连续运行多个命令,但是其中一些命令运行的时间比较长,而你不想长时间地守候在计算机旁,这个时候应该怎么办呢?例如,如果一个zip压缩文件中有很多John Coltrane的MP3文件,你想先解压缩,再把它们放到一个新子目录中,最后再删除原来的压缩文件,该怎么做呢?通常你只能一次运行一个命令,如下所示:

说明 为了节省篇幅,此处删除了完整列表中的所有者和组信息。

$ ls -l /home/scott/music
-rw-r--r-- 1437931 2005-11-07 17:19 JohnColtrane.zip
$ unzip /home/scott/music/JohnColtrane.zip
$ mkdir -p /home/scott/music/coltrane
$ mv /home/scott/music/JohnColtrane*.mp3
➥/home/scott/music/coltrane/
$ rm /home/scott/music/JohnColtrane.zip

JohnColtrane.zip的文件大小为1.4 GB,即使是速度很快的计算机,解压这么个庞然大物也要花不少时间,而且除了坐在那里干等,你或许有更重要的事情去做。命令栈(command stacking)可以解决这一问题。

命令栈是将所有需要运行的命令放到shell的一行上,再用分号(;)隔开每个具体的命令。接着依次顺序执行每个命令,只有一个命令结束运行(无论成功或失败),才会运行下一个命令。这样的用法很容易,也确实能够节省你的时间。

用命令栈的方式,前面的一系列命令可以写成:

$ ls -l /home/scott/music
-rw-r--r-- 1437931 2005-11-07 17:19 JohnColtrane.zip
$ unzip /home/scott/music/JohnColtrane.zip ;
➥mkdir -p /home/scott/music/coltrane ;
➥mv /home/scott/music/JohnColtrane*.mp3
➥/home/scott/music/coltrane/ ;
➥rm /home/scott/music/JohnColtrane.zip

当然,在命令运行的时候,也可以使用这个办法来引入短暂的延迟。如果你想截取显示器中所有可见内容的截图,只需要运行以下命令(假设你的计算机中已经安装了ImageMagick包,几乎所有的Linux分发版中都有这个包):

$ sleep 3 ; import -frame window.tif

这个例子中的sleep命令会等待3秒钟,然后使用import命令截取屏幕截图。有几秒钟的延迟,你就有时间最小化终端控制台应用程序,把你想让截图中出现的任何窗口调到前面来。分号(;)便于从逻辑上分隔各个命令,这样你就能最大程度地使用这些命令。

警告 使用命令栈要十分谨慎,特别是删除或者移动文件的时候。确保你输入的确实是你想要运行的命令,因为这些命令是依次顺序运行的,否则可能会得到意想不到的结果。

&&

在上一节中用;分隔命令,如下所示:

$ unzip /home/scott/music/JohnColtrane.zip ;
➥mkdir -p /home/scott/music/coltrane ;
➥mv /home/scott/music/JohnColtrane*.*mp3
➥/home/scott/music/coltrane/ ;
➥rm /home/scott/music/JohnColtrane.zip

但是如果你敲命令时稍不留神,输成了下面这样:

$ unzip /home/scott/JohnColtrane.zip ;
➥mkdir -p /home/scott/music/coltrane ;
➥mv /home/scott/music/JohnColtrane*.*mp3
➥/home/scott/music/coltrane/ ;
➥rm /home/scott/music/JohnColtrane.zip

原本应该输入unzip /home/scott/music/JohnColtrane.zip,结果无意间输成了unzip /home/scott/JohnColtrane.zip,而你没有注意到这一点,继续操作并按Enter键,之后就起身走开了。计算机不能执行unzip /home/scott/JohnColtrane.zip这条命令,因为文件根本不存在,之后它悠然自得地继续执行下一条命令(mkdir),执行这条命令没什么问题。但是第三条命令(mv)就不能执行了,因为unzip没有运行成功,根本就不存在任何MP3文件。最后开始执行第四条命令,删除zip文件(注意,是这次你提供的路径是正确的),而你没办法恢复和重新来一次了。这下糟了!

说明 不相信会发生这样的事件链吗?就在几天前我还犯过非常类似的错误呢。嗯,我都觉得自己像白痴一样。

之所以出现这样的问题,是因为使用“分号(;)”的命令是顺序执行的,但没有考虑它们的执行成功与否。分隔命令的一个更好的办法就是用&&,它同样也是依次顺序运行每个命令,但只有当前面一条命令运行成功之后,才能执行下一条命令[从技术上讲,每条命令返回的退出状态(exit status)码必须为0,才能运行下一条命令]。如果一条命令运行失败,整个命令链就会停下来。

如果在上个例子中使用&&代替;来顺序执行命令,看起来应该是以下这个样子:

$ unzip /home/scott/JohnColtrane.zip &&
➥mkdir -p /home/scott/music/coltrane &&
➥mv /home/scott/music/JohnColtrane.*mp3
➥/home/scott/music/coltrane/ &&
➥rm /home/scott/music/JohnColtrane.zip

由于第一个unzip命令没有成功完成,整个过程就会停止。你回来以后,会发现那串命令运行失败了,但JohnColtrane.zip文件依然存在,所以还可以再试次一次。这就好多了!

下面再举两个例子,让你看看&&的用处有多大。第13章会介绍apt,它是一种升级基于Debian的Linux系统的好方法。使用apt时,首先更新可用软件的列表,然后查找是否有任何可用的更新。如果不能更新软件列表,显然就不必再费事地查找更新了。为了确保第二步处理不会发生(这一步没有用),使用&&分隔命令:

# apt-get update && apt-get upgrade

另一个例子:如果你想使用ps2pdf命令把PostScript文件转换成PDF,打印生成的PDF文件,之后删除PostScript文件。创建这些命令组合,最好的方法就是使用&&

$ ps2pdf foobar.ps && lpr foobar.pdf && rm foobar.ps

如果使用;,万一ps2pdf命令失败,PostScript文件将被彻底删除,让你想再来一次都没办法。

现在你确信&&通常是更好的办法了吗?如果删除文件不会带来什么危险,使用;可能也行,但是如果其中某个命令涉及rm,或其他什么无法恢复的操作,最好还是使用&&,这样更安全些。

||

仅当前一个命令运行成功时,&&才会依次运行接下来的命令。||则正好相反:只有第一个命令失败了(从技术上讲,命令返回的退出状态码不为0),第二条命令才会运行。可以把它看作“不是……就是……”这样的短语:不是运行第一个命令,就是运行第二个命令。

当一个处理失败时,经常用||给管理员发送警告。例如,为了确保特定的计算机开启和正在运行,管理员可能不时地使用ping命令对它进行查询(第14章将详细讲述ping命令)。如果ping不通,则发送一封电子邮件给管理员,通报这一情况。

ping -c 1 -w 15 -n 72.14.203.104 || 
{
  echo “Server down” | mail -s 'Server down' admin@google.com
}

说明 想知道|是什么吗?参见4.6节,就能知道它是什么以及怎么用。

稍微思考一下,你会发现||可以用于很多地方。它是一个功能强大的工具,真的很有用。

$()

命令替换(command substitution)是接受一个命令的输出,并将它插入到另一个命令中,好像你直接手工输入这一输出的内容一样。用$()将最初运行的命令括起来(这个命令将产生要被插入的输出内容)。通过下面的例子可以说得更明白些。

假设你刚参加完一个家庭聚餐,将你的数码相机连到Linux计算机,把新照片复制出来,把它们放到一个以当天日期来命名的文件夹中。

$ pwd
/home/scott/photos/family
$ ls -1F
2005-11-01/
2005-11-09/
2005-11-15/
$ date “+%Y-%m-%d”
2005-11-24
$ mkdir $(date “+%Y-%m-%d”)
$ ls -1F
2005-11-01/
2005-11-09/
2005-11-15/
2005-11-24/

在这个例子中,首先运行的是date "+%Y-%m-%d",然后mkdir用这个命令的输出(2005-11-24)来作为新目录的名称。这是个强大的功能,当你阅读其他人编写的shell脚本时(Web上很容易找到这些脚本),会发现到处都在用命令替换。

说明 过去曾经建议用单撇号(`)将最初的命令括起来,单撇号字符位于键盘的左上方。不过,现在最好还是使用本节采用的字符 $()

要充分利用本章讲述的这些信息,就需要理解Linux shell的三种输入/输出流:标准输入流(standard input)、标准输出流(standard output)和标准错误流(standard error)。每种输入/输出流都有一个文件描述符(或数字标识符)、一个常用的缩写名和一个可用的默认设备。

例如,用键盘输入时,其实就是将输入发送到标准输入流,缩写为stdin,标识为0。当计算机将输出显示到终端时,使用的就是标准输出流,缩写为stdout,标识为1。如果计算机需要报告错误,并在终端上显示错误信息,使用的就是标准错误流,缩写为stderr,标识为2。

使用ls这个普通的命令,查看这三种输入/输出流。在键盘上输入ls,使用的是stdin。输入ls后按Enter键,目录中文件和文件夹的列表通过stdout显示出来。如果对并不存在的文件夹来运行ls命令,终端上出现的错误信息就是通过stderr而提供的。

表4-1能更直观地帮助你理解这三种流。

表4-1 三种输入/输出流
文件描述符(标识符) 名  称 常用缩写名 典型的默认设备
0 标准输入流 stdin 键盘
1 标准输出流 stdout 终端
2 标准错误流 stderr 终端

在本章中,我们将学习如何重定向输入和输出。例如,不让输出呈现在终端上,可以将输出重定向到其他程序。不从键盘接受输入,程序从文件获取输入。在理解了stdin和stdout的窍门以后,你就能够做很多功能强大的事情了。

|

Unix是由一些小片段松散连接在一起的,这是句真理。除了管道(pipe)的概念,没有什么比它更能体现这一原则了。管道就是键盘上的“|”符号,当把它放在两个命令之间时,它可以将第一个命令的输出作为第二个命令的输入。换句话说,|重定向stdout,将它发送到第二个命令的stdin。

下面用一个简单的例子来帮助你更好地理解这个概念。ls命令是我们早已知道的,在第5章中还将讲述less命令。现在只需要知道less命令能够让用户在屏幕一次查看文本文件的一页。如果在包含很多文件的目录上运行ls命令,例如/usr/bin,显示内容就会因为速度太快而不能阅读。但是如果将ls命令的输出通过管道发送给less命令,就可以在屏幕以一次阅读一页的方式来查看结果了。

$ pwd
/usr/bin
$ ls -1

zipinfo
zipnote
zipsplit
zsoelim
zxpdf
[Listing truncated due to length - 2318 lines!]
$ ls -1 | less
411toppm
7z
7za
822-date
a2p

通过管道将ls -1的结果传递给less,就可以一次在屏幕上只查看结果的一页,这样用起来要容易得多。

接下来是一个更高级的例子,使用了两个后面将会讨论的命令psgrep。第12章将介绍ps命令,它能够列出正在运行的进程;第9章介绍grep命令,可以用它查找文件中与指定模式相匹配的行。假设Firefox运行异常,你怀疑后台仍旧有多个Firefox程序的副本在运行。ps命令会列出计算机中正在运行的所有进程,但是输出的结果总是很长,而且还在不断地变化。如果将ps的输出通过管道传递给grep命令,让它去搜索firefox,这样,Firefox确实还在运行的话,你就能够马上做出判断了。

说明 为了节约篇幅,在以下代码中删除了所有者的信息,每个实例的所有者都是同一个用户。

$ ps ux
1504  0.8  4.4  75164 46124 ? S Nov20 1:19 kontact
19003  0.0  0.1   3376  1812 pts/4 S+ 00:02 0:00 ssh
➥admin@david.hartley.com
21176  0.0  0.0      0    0 ? Z 00:14 0:00
➥[wine-preloader] <defunct>
24953  0.4  3.3  51856  34140 ? S 00:33 0:08 kdeinit:
➥kword /home/scott/documents/clientele/current
[Listing truncated for length]
$ ps ux | grep firefox
scott  8272  4.7 10.9 184072 112704 ? Sl Nov19 76:45
➥/opt/firefox/firefox-bin

将原本有58行的输出结果变成了一行,现在读起来容易多了。

说明 要记住,虽然很多程序都能够用管道,但并非所有程序都如此。例如,文本编辑器vim(或pico、nano及emacs)会接管整个shell,因此,键盘的所有输入都直接发送给vim,而所有输出都在程序的某个位置进行显示。因为vim完全控制着shell,所以也就不能使用程序通过管道重定向它的输出。多使用几次shell,你就能逐渐学会如何识别不能使用管道的程序了。

>

通常,输出是显示在屏幕上的,因此称其为标准输出(stdout)设备。如果不想将输出结果显示到屏幕上,则可以使用>(大于号)字符将输出存到文件中。

$ pwd
/home/scott/music
$ ls -1F
Hank_Mobley/
Horace_Silver/
John_Coltrane/
$ ls -1F Hank_Mobley/* > hank_mobley.txt
$ cat hank_mobley.txt
1958_Peckin'_Time/
1960_Roll_Call/
1960_Soul_Station/
1961_Workout/
1963_No_Room_For_Squares/

$ ls -1F
Hank_Mobley/
hank_mobley.txt
Horace_Silver/
John_Coltrane/

注意,在使用>之前,文件hank_mobley.txt并不存在。当使用>将输出重定向到并不存在的文件时,就会创建这个文件。特别需要注意的是,如果文件hank_mobley.txt早已存在,它将被完全覆盖。

警告 再次重申,使用重定向要十分谨慎,因为这有可能损坏原来保存重要内容的文件。

不过,有一种办法可以防止重定向时覆盖文件,即noclobber选项。如果将noclobber设置为onbash就不允许重定向覆盖已经存在的文件,除非得到你的明确许可。要打开noclobber,使用以下命令:

$ set -o noclobber

从现在起,如果要使用重定向并覆盖文件,应该用>|代替>,如下所示:

$ pwd
/home/scott/music
$ ls -1F
Hank_Mobley/
hank_mobley.txt
Horace_Silver/
John_Coltrane/
$ ls -1F Hank_Mobley/* > hank_mobley.txt
ERROR
$ ls -1F Hank_Mobley/* >| hank_mobley.txt
$ cat hank_mobley.txt
1958_Peckin'_Time/
1960_Roll_Call/
1960_Soul_Station/
1961_Workout/
1963_No_Room_For_Squares/

如果觉得不喜欢或不需要noclobber,可以把它关了:

$ set +o noclobber

要一直开着noclobber选项,需要在.bashrc文件中增加一行set -o noclobber

>>

前面讲述了>符号可以将输出从stdout重定向到文件。例如,可以非常容易地将date命令的输出重定向到文件:

$ date
Mon Nov 21 21:33:58 CST 2005
$ date > hank_mobley.txt
$ cat hank_mobley.txt
Mon Nov 21 21:33:58 CST 2005

记住,使用>重定向时,如果文件不存在,就会创建一个新文件;如果文件已经存在,则会覆盖已有的文件。但是,如果使用>>来代替>,就会把输出追加到指定文件的底部(如果文件不存在,就会创建它)。

$ cat hank_mobley.txt
Mon Nov 21 21:33:58 CST 2005
$ ls -1F Hank_Mobley/* >> hank_mobley.txt
$ cat hank_mobley.txt
Mon Nov 21 21:33:58 CST 2005
1958_Peckin'_Time/
1960_Roll_Call/
1960_Soul_Station/
1961_Workout/
1963_No_Room_For_Squares/

警告 慎用>>。如果无意间输成了>,就不是追加,而是覆盖文件!

<

通常,通过键盘提供命令的输入,因此称其为标准输入(stdin)设备。与将stdout重定向到文件一样,也可以重定向stdin,让它接受来自文件而不是键盘的输入。这有什么用呢?有些命令不能直接打开文件,在这种情况下,<(小于号)就是你需要的解决方法。

例如,通常用echo命令来重复显示在stdin中输入的内容,如下所示:

$ echo "This will be repeated"
This will be repeated.

但是,你可以用<重定向输入,让echo命令使用文件的内容,而不是stdin输入的内容。在这个例子中,我们使用前一节创建的hank_mobley.txt文件。

$ echo < hank_mobley.txt
Mon Nov 21 21:33:58 CST 2005
1958_Peckin'_Time/
1960_Roll_Call/
1960_Soul_Station/
1961_Workout/
1963_No_Room_For_Squares/

虽然不会一直使用<,但是在一些情况下还是必需的,所以记住它吧。

虽然本书开始介绍的命令都比较简单,但使用本章介绍的这种 “搭积木”式组合命令的方法,可以以一种新的、有趣的方式组合这些命令。随着学习的深入,后面讲述的内容会越来越复杂,但功能也会越来越强大。而要获得更加强大的功能,本章讲述的内容是一个关键因素。现在,继续前进!



第二部分 使用文件

第5章 查看文件

第6章 打印和管理打印任务

第7章 拥有者和权限

第8章 归档和压缩



第5章 查看文件

大多数Linux计算机的优点之一就是,几乎所有重要的系统配置、日志和信息文件都是ASCII文本格式的。因为ASCII是一种被很好地支持的、古老的(从计算机发展的角度来看)标准,所以可以用很多软件命令来查看这些文本文件的内容。本章介绍在阅读ASCII文本文件时经常使用的4个命令。

cat

DOS用户使用type命令将文本文件的内容显示在屏幕上。Linux用户则可以使用cat命令,它做的事情与type命令一样。想在shell中查看文件吗?试试cat命令吧。

$ cat Hopkins_-_The_Windhover.txt
I caught this morning morning's minion, kingdom
of daylight's dauphin, dapple-dawn-drawn Falcon, in his riding
  Of the rolling level underneath him steady air, and striding
High there, how he rung upon the rein of a wimpling wing
In his ecstasy! then off, off forth on swing,
  As a skate's heel sweeps smooth on a bow-bend: the hurl and gliding
  Rebuffed the big wind. My heart in hiding
Stirred for a bird, -- the achieve of, the mastery of the thing!

Brute beauty and valour and act, oh, air, pride, plume, here
  Buckle! AND the fire that breaks from thee then, a billion

Times told lovelier, more dangerous, o my chevalier!
  No wonder of it: sheer plod makes plough down
sillion
Shine, and blue-bleak embers, ah my dear,
  Fall, gall themselves, and gash gold-vermilion.
$

cat命令将文件打印到屏幕,然后直接返回到命令提示符。如果文件长度超出屏幕的范围,必须向上滚动才能看到刚才一闪而过的内容。

这正是使用cat命令的一个大问题:如果正在查看的文档很长,文件内容在屏幕上飞驰而过,这个过程可能要花不少时间,同时也很难阅读(你能想象这个命令会产生什么:cat Melville_-_Moby_ Dick.txt)。解决办法就是使用5.5节中介绍的less命令。

cat file1 file2

cat是concatenate(拼接)的缩写,意思是“连接到一起”。cat命令最初的用途是将两个或更多的文件拼接成一个文件只对一个文件应用(cat命令并把其内容打印到屏幕上,属于设计之外的用法)。例如,假设你有一首A. E. Housman的短诗和一首Francis Quarles的短诗,想同时查看它们。

$ cat housman_-_rue.txt quarles_-_the_world.txt
WITH rue my heart is laden
  For  golden friends I had,
For many a rose-lipt maiden
  And many a lightfoot lad.
By brooks too broad for leaping
  The lightfoot boys are laid;
The rose-lipt girls are sleeping
  In fields where roses fade.
The world's an Inn; and I her guest.
I eat; I drink; I take my rest.
My hostess, nature, does deny me
Nothing, wherewith she can supply me;
Where, having stayed a while, I pay
Her lavish bills, and go my way.

注意,cat命令在显示时并没有用横线、破折号及类似的符号来分隔两个文件。实际上,cat命令将两个文件糅合在一起,再显示出来。如果想把文件隔开(短诗“With rue my heart is laden”最后一行跟着“On the world”的第一行很难阅读),需要确保每个拼接在一起的文件后面都留有一个空白行。

cat file1 file2 > file3

在上一个例子中,将两个文件拼接起来,并将它们打印到屏幕这个stdout设备上。但是这可能不是你想要的效果。将两个文件拼接起来,最好还能将新连接的文件保存成另一个可以使用的文件。为此,需要使用第4章中学到的方法,将输出结果由stdout重定向到一个文件。

$ ls
housman_-_rue.txt quarles_-_the_world.txt
$ cat housman_-_rue.txt quarles_-_the_world.txt > poems.txt$ ls
housman_-_rue.txt poems.txt quarles_-_the_world.txt

现在,你就可以随意使用poems.txt这个文件了。如果想在这个文件中增加更多短诗,操作起来很容易,如下所示:

$ cat housman_-_one-and-twenty.txt >> poems.txt

注意,这一次使用>>将新的短诗追加到poems.txt中。下面的命令不会运行成功:

$ cat housman_-_one-and-twenty.txt poems.txt > poems.txt

如果想把文件和它自身拼接起来,就会看到这个错误消息,解释为什么不能完成请求:

cat: poems.txt: input file is output file

cat -n file1 file2

写诗和写源代码一样,加上行号会非常好,可以使引用更明确。如果使用cat命令时想生成行号,可以增加-n选项(或--number)。

$ cat -n housman_-_rue.txt quarles_-_the_world.txt
    1  WITH rue my heart is laden 
    2    For golden friends I had,
    3  For many a rose-lipt maiden
    4    And many a lightfoot lad.
    5  By brooks too broad for leaping
    6   The lightfoot boys are laid;
    7  The rose-lipt girls are sleeping
    8   In fields where roses fade.
    9  The world's an Inn; and I her guest.
   10  I eat; I drink; I take my rest.
   11  My hostess, nature, does deny me
   12  Nothing, wherewith she can supply me;
   13  Where, having stayed a while, I pay
   14  Her lavish bills, and go my way.

行号非常有用,cat命令提供了一种快捷的方法,可以在文件中加入行号。

说明 比cat命令更好的命令是dog命令(更多信息可以访问http://opensource.weblogsinc.com/2005/02/17/why-dogs-are-betters-than-cats)。除了本地文件,还可以用dog命令在stdout上查看网页HTML源代码,以及指定网页上的一系列图片或链接。dog命令能够把所有字符转换成小写字符,反之亦然;能够将行结束符转换成Mac OS、DOS、或者Unix系统中使用的符号;甚至能够指定要输出的字符的范围(例如,5~25行)。dog命令的man page也是非常有趣的东西,这更不必说了。这真是一只懂得很多窍门的小狗。

cat命令相对应,还创建了另外一个程序:tac。它的名称中的字母顺序正好与cat相反。tac的功能是:反向拼接文件①。它并不是你随时都需要使用的东西,不过现在先知道实现这个功能原来这么容易,在你需要它时就好办了。

cat是从第一行显示到最后一行,而tac则是从最后一行显示到第一行。——译者注

less file1

虽然cat命令有用,但是如果要阅读一个很长的文件,它就一点用都没有了,因为文本就像漫无止境、无法阅读的溪流一样流淌过去了。如果你有兴趣在命令行中查看长文件(所谓“长”,至少要超过一两个页面),就不应该使用cat命令,而是使用less命令。

less命令是分页器的一个例子,分页器是一个可以分页显示文本文件内容的程序。其他的命令还有morepgmost。事实上,作为改进的moreless命令发布于1985年,这再一次证实了名言“少即是多(less is more)”!

less命令打开一个文件(甚至是像Milton的“Paradise Lost”这样的大文件)是再容易不过的事情了:

$ less Paradise_Lost.txt

less命令运行时会占据整个屏幕,所以必须使用键盘在less命令界面中导航,只有退出less命令才能回到命令行。表5-1列出了在less命令界面中导航时用到的一些键盘操作命令。

表5-1  less的键盘操作命令
键盘命令 操 作
PageDn、e或空格键 前进一页
PageUp或b 后退一页
Return、e、j或下箭头键 前进一行
y、k或上箭头键 后退一行
G或p 前进到文件的结尾
1G 回到文件的开始
Esc-)或右箭头键 向右滚动
Esc-(或左箭头键 向左滚动
Q 退出less

可以看到,大多数操作命令都有多个选择。或许向下翻页和退出程序是最常使用的两个键盘命令。

要在less命令界面中查看文件信息,可以按“=”(等号)键,就会在屏幕的底部显示如下类似的信息:

Paradise_Lost.txt lines 7521-7560/10762 byte 166743
➥/237306 70% (press RETURN)

可以看到,这一信息提示按Enter键来去掉显示的数据,并返回继续使用less

用与让cat命令插入文件的行号一样的方法,也可以命令less显示行号。当然只有在使用less命令时,这些数字才会出现。在按q键退出less后,这些数字就消失了。要查看文件,并且在每行前面显示行号,需要使用less命令的-N(或--LINE-NUMBERS)选项。注意,选项必须全大写:

$ less -N Paradise_Lost.txt

如果用less命令查看大型文件或者内容非常密集的文件,可能会很难找到你特别感兴趣的文字。例如,如果你想知道Milton在“Paradise Lost”中是否使用过apple这个词来描述Adam和Eve所吃的水果,该怎么办?在less命令界面中,按/键,然后再输入你要搜索的模式(如果愿意,甚至可以使用正则表达式)。输入好搜索模式后,按Enter键,less就会跳转到匹配搜索模式的第一个实例(如果存在的话)。如果在文件中没有找到任何能够匹配搜索模式的实例,less会提示以下信息:

Pattern not found (press RETURN)

重复搜索也很简单,在文件中既可以向前搜索,也可以向后搜索。表5-2列举了在less命令中进行搜索时要用到的主要命令。

表5-2  less的搜索命令
键盘命令 操  作
/搜索模式 向前搜索使用正则表达式来表示的模式
n 向前重复搜索
N 向后重复搜索

说明 实际上,Milton从来都没有明确地将水果(fruit)称为苹果(apple),只是“水果”而已。我攻读17世纪英国文学的博士学位有些年头了,但是还没有读完,这也正是我能够编写这本书,却写不出专门致力于研究John Milton 和王政复辟时期文学(Restoration literature)的著作的一个原因。

less本身并不是编辑器,它只是一个查看器,但是可以通过按v键,将使用less正在查看的文件传递给文本编辑器(如vim或nano),进行编辑处理。先用less查看一个文件,然后按v键。一两秒钟以后,less界面消失,出现一个全屏的文本编辑器。编辑完文件后,退出编辑器,就可以返回less命令界面,而且也可以看到刚才对文件所做的修改。

如果你不喜欢使用按v键时出现的那个编辑器,也可以将它改为自己选择的编辑器。例如,如果你想使用vim,在使用less之前运行以下命令:

$ export EDITOR=vim

每个会话(session)只需要运行一次上述命令,之后每次打开lessvim就是默认的编辑器。但是如果结束会话,下次就需要再输入export命令,这很快就会让人感到厌烦。更好的方法是将下面这行配置添加到.bashrc文件中,这样每次启动新的会话时都可以自动应用这一配置:

export EDITOR=vim

head

如果你只需要查看一个文件的前10行内容,例如Chaucer的“Canterbury Tales”,就没有必要使用cat命令或less命令,使用head命令就可以了。head命令可以只打印出文件的前10行内容,然后返回到命令行。

$ head Canterbury_Tales.txt
Here bygynneth the Book of the Tales of Caunterbury

General Prologue

Whan that Aprill, with his shoures soote
The droghte of March hath perced to the roote
And bathed every veyne in swich licour,
Of which vertu engendred is the flour;
Whan Zephirus eek with his sweete breeth
Inspired hath in every holt and heeth
$

要快速浏览文本文件,head命令非常棒,即使文件很大也没有关系。只需要一眨眼的时间,你就能知道这个文件是否是你需要的文件。

head file1 file2

可以用head命令一次查看多个文件的前10行内容。这听起来与cat命令有点相似,确实如此。不过,head命令能够提供一个标题(header)以分隔各个文件,这样就能够容易地区分各个文件。下面是Chaucer的“Canterbury Tales”和Milton的“Paradise Lost”前10行的内容。

$ head Canterbury_Tales.txt Paradise_Lost.txt
==> Canterbury_Tales.txt <==
Here bygynneth the Book of the Tales of Caunterbury

General Prologue

Whan that Aprill, with his shoures soote
The droghte of March hath perced to the roote
And bathed every veyne in swich licour,
Of which vertu engendred is the flour;
Whan Zephirus eek with his sweete breeth
Inspired hath in every holt and heath

==> Paradise_Lost.txt <==
Book I

Of Man's first disobedience, and the fruit
Of that forbidden tree whose mortal taste
Brought death into the World, and all our woe,
With loss of Eden, till one greater Man
Restore us, and regain the blissful seat,
Sing, Heavenly Muse, that, on the secret top
Of Oreb, or of Sinai, didst inspire
That shepherd who first taught the chosen seed

head命令自动用一个空行和标题来分隔两段摘录,这样就能够清楚区分不同的文件。

如果不想查看文件的前10行内容,可以使用-n选项,后面跟上一个数字,比如5(或者用--lines=5),让head命令显示不同行数的内容。如果你指定两个或更多的文件,例如Chaucer的“Canterbury Tales”和Milton的“Paradise Lost”,结果将显示所有文件的前几行内容。

$ head -n 5 Canterbury_Tales.txt Paradise_Lost.txt
==> Canterbury_Tales.txt <==
Here bygynneth the Book of the Tales of Caunterbury

General Prologue

Whan that Aprill, with his shoures soote

==> Paradise_Lost.txt <==
Book I

Of Man's first disobedience, and the fruit
Of that forbidden tree whose mortal taste
Brought death into the World, and all our woe,

注意,这5行包括空白行和文本行。5行就是5行,不管这5行包含什么内容。

head -c

-n选项可以指定查看文件前面几行的内容,但是如果想查看一定字节数量的内容呢?或者几K字节的内容?甚至是几兆字节的内容(其实这样做挺傻,因为屏幕会不停地向上滚动)?可以使用-c(或--bytes=)选项。

要查看Chaucer的“Canterbury Tales”前100个字节的内容,可以使用以下命令:

$ head -c 100 Canterbury_Tales.txt
Here bygynneth the Book of the Tales of Caunterbury
General Prologue

Whan that Aprill, with his sh

100个字节就是100个字节,如果这种方法会把最后一个词从中间分开了,也只能就这样了。

要查看Chaucer的“Canterbury Tales”前100 KB的内容,可以使用以下命令:

$ head -c 100k Canterbury_Tales.txt
Here bygynneth the Book of the Tales of Caunterbury

General Prologue

Whan that Aprill, with his shoures soote
The droghte of March hath perced to the roote
And bathed every veyne in swich licour,
Of which vertu engendred is the flour;
Whan Zephirus eek with his sweete breeth
Inspired hath in every holt and heeth

而要查看Chaucer的“Canterbury Tales”前100 MB的内容,你可以使用……不!不要这样做,这可能把本书其余的内容都显示出来!但是如果你执意要这么做,可以试试以下的命令:

$ head -c 100m Canterbury_Tales.txt

这里的m表示1048576字节,或者是1024×1024字节。

tail

head命令可以查看一个文件前10行的内容,而按照典型的异想天开式的Unix风格,还可以使用tail命令来查看一个文件最后10行的内容。从headtail,搞懂了吗?

$ tail Paradise_Lost.txt
To the subjected plain - then disappeared
They, looking back, all the eastern side beheld
Of Paradise, so late their happy seat,
Waved over by that flaming brand; the gate
With dreadful faces thronged and fiery arms.
Some natural tears they dropped, but wiped them soon;
The world was all before them, where to choose
Their place of rest, and Providence their guide.
They, hand in hand, with wandering steps and slow,
Through Eden took their solitary way.

为什么要使用tail命令呢?通常,查看日志(log)文件的最后几行内容,才能发现应用程序或者系统的最新运行状况。当然,在这种情况下你还可以使用另一个非常重要的选项,这将在5.15节中介绍。

tail file1 file2

使用head命令可以一次查看多个文件的前10行内容,同样,使用tail命令也可以做类似的事,这一点儿也不奇怪。

$ tail Paradise_Lost.txt Miller's_Tale.txt
==> Paradise_Lost.txt <==
To the subjected plain - then disappeared
They, looking back, all the eastern side beheld
Of Paradise, so late their happy seat,
Waved over by that flaming brand; the gate
With dreadful faces thronged and fiery arms.
Some natural tears they dropped, but wiped them soon;
The world was all before them, where to choose
Their place of rest, and Providence their guide.
They, hand in hand, with wandering steps and slow,
Through Eden took their solitary way.
==> Miller's_Tale.txt <==
With othes grete he was so sworn adoun
That he was holde wood in al the toun;
For every clerk anonright heeld with oother.
They seyde, “The man is wood, my leeve brother”;
And every wight gan laughen at this stryf.
Thus swyved was this carpenteris wyf,
For al his kepyng and his jalousye;
And Absolon hath kist hir nether ye;
And Nicholas is scalded in the towte.
This tale is doon, and God save al the rowte!

head命令一样,tail命令也使用标题(分隔符)来分隔不同的文件,这非常有用。

tail -n

继续介绍head命令和tail命令的相似之处。除了接受默认的10行,也可以使用-n(或--lines=)选项来指定想要查看的文件行数。想要查看多个文件的后面几行?只要把多个文件名加到命令中就可以了。

$ tail -n 4 Paradise_Lost.txt Miller's_Tale.txt
==> Paradise_Lost.txt <==
The world was all before them, where to choose
Their place of rest, and Providence their guide.
They, hand in hand, with wandering steps and slow,
Through Eden took their solitary way.
==> Miller's_Tale.txt <==
For al his kepyng and his jalousye;
And Absolon hath kist hir nether ye;
And Nicholas is scalded in the towte.
This tale is doon, and God save al the rowte!

想查看多个日志文件吗?这是个挺有用的命令。但它还不完美,要看看完美的命令,请阅读下一节。

tail –f

tail -f --pid=PID# terminates after PID dies.

日志文件的最大特点就是,它会随着系统中各种事情的发生而不断更新。tail命令可以显示文件的一个快照(snapshot),然后直接返回到命令行。想要再次查看日志文件?不断地运行tail命令,一次,又一次。真麻烦!

使用-f(或--follow)选项,tail命令就不会自动关闭了。每当文件发生变化时,它就会显示文件的最后10行(或在选项中增加-n,指定不同的数字)的内容。用这种方法就可以观察到日志文件随时发生的所有变化。如果你正想弄明白系统或者程序刚刚发生了什么情况,这个命令就非常有用。

例如,Web服务器的日志文件可能如下所示:

说明 为了节省篇幅,删除了访问的IP地址、日期和时间。

$ tail -f /var/log/httpd/d20srd_org_log_20051201
“GET /srd/skills/bluff.htm HTTP/1.1”...
“GET /srd/skills/senseMotive.htm HTTP/1.1”...
“GET /srd/skills/concentration.htm HTTP/1.1”...
“GET /srd/classes/monk.htm HTTP/1.1”...
“GET /srd/skills/escapeArtist.htm HTTP/1.1”...

很难在书中展示这个命令运行时的动态效果,这个文件也不会关闭。tail命令会一直打开它,以确保任何新的变化都能及时显示出来。文件显示的内容不断向上滚动,直到按Ctrl+c键才会停下来,并返回到命令行。

用一个日志文件来试试,例如/var/log/syslog。增加n选项,看看是否开始只显示特定行数的内容。然后用两个文件来试试这个命令,例如/var/log/syslog/var/log/daemon.log,看看会发生什么事情(提示:将会与5.9节中的情形非常相似,但是这里它会不断地更新)。

本章讲述了4个命令:catlesshead以及tail。它们都能以只读模式显示文本文件的内容,但是显示的方法各不相同。cat命令一次显示整个文件的内容,而less命令则以分页方式一次一屏地显示内容。head命令和tail命令就好比是一枚硬币(或者是同一文件)的两面,前者用于查看文件的开始部分,后者则显示文件结尾部分。总之,把这4个命令结合起来使用,就可以方便地查看文本文件中的任何部分。



第6章 打印和管理打印任务

多年来,Linux已经拥有了多个打印系统,包括历史悠久的LPD(Line Printer Daemon)和LPRng(LPR Next Generation),现在的各种Linux分发版中依然能够发现它们的踪影。不过,在过去的几年中,大多数Linux分发版已经将CUPS(Common Unix Printing System)作为其后台系统的首选。CUPS被很好地支持,而且易于使用,是LPD和LPRng非常棒的替代产品。LPD和LPRng命令在CUPS中仍然可以使用,只不过现在它们调用的都是CUPS中的功能。

本章主要介绍大多数Linux用户使用的打印系统——CUPS,并没有介绍如何安装和配置打印机。现在大多数Linux分发版都提供了好用的GUI(图形用户界面)配置工具来完成这些操作,所以本章以如何通过命令行来实际查询和使用打印机为主要内容。

说明 Linux Journal的一篇文章“Overview of Linux Printing System”(www.linuxjournal.com/article/6729)简单介绍了可供现在Linux用户挑选的各种选择,重点介绍了目前最受欢迎的CUPS。有关CUPS的更多信息,参阅读Linux Journal的“The CUPS Printing System”(www.linuxjournal.com/article/8618),它精辟地阐述了这种技术的本质。获取有关CUPS的信息,最好的地方莫过于CUPS Software Users Manual,网址为www.cups.org/doc-1.1/sum.html。这个手册比较长,而且有的内容有些含糊,但手册中有很多有价值的建议和帮助,是有关CUPS的重要资源。

lpstat -p

在开始使用打印机之前,需要先知道系统中已经安装了哪些打印机。要找到在系统中配置好的打印机,可以使用带有-p选项的lpstat(line printer status的缩写)命令。

$ lpstat -p
printer bro is idle. enabled since Jan 01 00:00
printer bro_wk is idle. enabled since Jan 01 00:00
printer wu is idle. enabled since Jan 01 00:00

可以看到,这个系统中已经安装了三台打印机,分别为brobro_websanitywu_eads14。此时它们都没在打印东西。

lpstat -d

运行lpstat–p,可以知道系统中安装了哪些打印机,但是哪个是默认的呢?很快,你将看到可以将打印任务发送到指定的打印机,或者快速地将它发送给默认的打印机。要找出默认的打印机,可以使用带有-d(表示default)选项的lpstat命令。

$ lpstat -d
system default destination: bro

当然,如果计算机只连接了一台打印机,可能就不需要运行这个命令了。但是对于经常在不同位置之间移动的笔记本计算机用户,以及使用不同打印机打印的用户来说,这个命令是必需的。

lpstat -s

笔记本计算机用户将会发现接下来介绍的这个命令非常有用,因为它能够告诉他们如何访问可用的打印机。在第一次安装一台打印机时,必须指定如何连接到它。可以有如下几种选择。

  • 本地(通过并口、串口或USB接口连接)
  • 远程LPD队列
  • SMB共享打印机(Windows系统)
  • 网络打印机(TCP)
  • 远程CUPS服务器(IPP/HTTP)
  • 使用IPP的网络打印机(IPP/HTTP)

要找出计算机上配置了哪些打印机,以及如何连接到这些打印机,可以使用带有-s选项的lpstat命令。

$ lpstat -s
system default destination: bro
device for bro: socket://192.168.0.160:9100
device for bro_wk: socket://192.168.1.10:9100
device for wu: socket://128.252.93.10:9100

在这个例子中,每个打印机都是网络打印机,所以使用socket://,后面跟着每个打印机的IP地址和端口号(9100是大多数网络打印机的标准端口,可能有时也能够看到使用35端口的情况)。现在还非常简单,不过情况马上就变得复杂了。

虽然CUPS在很多情况下都是用户友好的,但是当涉及使用URI(Uniform Resource Indicators,统一资源定位符)标识Linux计算机中打印机的位置时,它出奇地迟钝。表6-1列出了你可能见过的每种连接方法和URI类型,可以帮助理解运行lpstat –s命令时看到的打印机和URI列表。

说明 假设在接下来的例子中,使用的打印机的名称是bro,其网络IP地址为192.168.0.160。当然,这一假设并不是在每种情况下都有用。如果打印机是通过并行接口的电缆连接的,那么它的IP地址则无关紧要。

表6-1 打印机连接和CUPS URI
连接方法 URI示例(打印机bro位于192.168.0.160)
并行接口 parallel:/dev/lp0
串行接口 serial:/dev/ttyS1?baud=115200
USB接口 usb:/dev/usb/lp0
远程LPD队列 lpd://192.168.0.160/LPT1
SMB共享打印机

(Windows系统)
smb://username:password@192.168.0.160/bro
网络打印机(TCP) socket://192.168.0.160:9100
远程CUPS服务器(IPP/HTTP) ipp://192.168.0.160:631/printers/bro、

http://192.168.0.160/printers/bro
使用IPP的网络打印机(IPP/HTTP) ipp://192.168.0.160:631/printers/bro、

http://192.168.0.160/printers/bro

由于过去几年中网络打印的发展,现在通过socketipp、或http等方式连接到打印机已经变得越来越简单了。尽管如此,你仍然可能会遇到传统打印机(legacy printer),它们需要使用旧的、更复杂的连接方法,所以让自己先熟悉它们还是不错的。

提示 有一个额外的收获就是,lpstat –s本质上复制了lpstat-p–d的功能,除了默认打印机以外,它也列出了系统中能识别出的所有打印机。如果你想快速获取打印机的所有信息,这是一个非常好用的命令。

lpstat -t

现在介绍一个功能强大的命令。使用lpstat –plpstat –dlpstat –s的好处是,你可以准确地得到自己想要的信息。但是,如果你想一次获取打印机的所有信息,则可以使用带有-t选项的lpstat命令,它可以将lpstat知道的打印机的所有信息都显示在shell中。

$ lpstat -t
scheduler is running
system default destination: bro
device for bro: socket://192.168.0.160:9100
device for bro_wk: socket://192.168.1.10:9100
device for wu: socket://128.252.93.10:9100
bro accepting requests since Jan 01 00:00
bro_wk accepting requests since Jan 01 00:00
wu accepting requests since Jan 01 00:00
printer bro is idle. enabled since Jan 01 00:00
printer bro_wk is idle. enabled since Jan 01 00:00
printer wu is idle. enabled since Jan 01 00:00

这些信息为:默认的打印机、系统能识别的所有打印机列表、打印机的连接方法和位置及所有打印机的状态。计算机中配置的打印机越多,这个列表就越长。对有些人来说,这个命令提供的信息量可能过多了。所以,你可能还是觉得前面介绍的带有其他选项的lpstat用法是更好的选择。

lpr

既然你已经知道了系统中有哪些打印机,现在就应该真正使用一下它们了。打印到默认的打印机(由lpstat -d决定的那个打印机)很容易。

$ lpr Lovecraft_-_Call_of_Cthulhu.txt

即只有lpr和文本文件的名称。非常简单。

说明 你可能认为在命令行用CUPS只能够打印ASCII文本文件,如果知道还能够打印PDF或PostScript文件,可能会让你大吃一惊。但确实是这样的。不过,不要尝试打印Word、OpenOffice.org文档以及其他非基于文本的或非基于PostScript的文档,否则你的打印机将吐出一堆废纸。

lpr -P

从上一节中可以看出,打印到默认打印机是非常容易的。不过,如果你有多个打印机,想把文件打印到某个非默认打印机上,使用-P选项,后面跟上那个打印机的名称也很容易。

$ lpr -P bro_wk Lovecraft_-_Call_of_Cthulhu.txt

如果你不知道打印机的名称,可以先用lpstat -p命令查询一下,如6.1节所讨论的那样。

说明 注意,例子中的文件名用的是下划线而不是空格,这样使得在命令行中处理文件名要容易得多。如果文件名中包含空格,就必须使用以下方法来引用这个文件名:

  $ lpr -P bro_wk "Lovecraft - Call of Cthulhu.txt"

  $ lpr -P bro_wk Lovecraft\ -\ Call\ of\ Cthulhu.txt

如果文件名中有空格,这时使用tab键自动补齐(tab completion)的功能来输入文件名大概是最简单的方法了,这样就不需要自己输入全部的文件名了。例如,在输入lpr -P bro_wk Love以后,按Tab键,bash就会自动补齐文件名。有关Tab键自动补齐的更多信息,可以参阅www.slackbook.org/html/shell-bash.html#SHELL-BASH-TAB

lpr -#

如果你想打印多份文档,可以使用-#(井字符号)选项,后面跟上你需要打印的份数:

$ lpr -# 2 -P bro Lovecraft_-_Call_of_Cthulhu.txt

份数可以是从1到100的任何数字。想打印超过100份?只能重复执行这一命令,或编写一个shell脚本,或者干脆雇个专业的打印员。

lpq

如果你有多个打印任务需要排队打印,那么有可能想看看打印队列的内容。或许你想取消一个或多个打印任务(接下来的几节会对此进行更详细的介绍),或者想看看为什么某个打印任务花费了那么长的时间却还在打印,或者你只是想知道打印队列中还有多少个任务。lpq命令(表示“lp queue”)用于列出默认打印机上正在打印的所有任务。

$ lpq
bro is ready and printing
Rank   Owner Job File(s)                Total Size
active scott 489 Lovecraft_-_Call_of_C 108544 bytes

如果你想查看所有打印机上打印队列的状态,而不只是默认打印机的队列,可以在lpq命令后面加个-a(代表“all”)选项。

$ lpq -a
Rank   Owner Job File(s)                Total Size
active scott 489 Lovecraft_-_Call_of_C 108544 bytes
1st    scott 490 ERB_-_A Princess_of_M 524288 bytes

这里需要记住两件事。首先,lpq -a命令提供的结果列表有删节,所以,即便真实的文件名是Lovecraft_-_Call_of_Cthulhu.txt 和 ERB_-_A Princess_of_Mars.txt,你也看不到完整的名称,因为lpq只能显示一定数量的字符。

其次,而且也是非常重要的一点,lpq命令显示的不是打印机知道的所有打印任务,它只能显示你的计算机知道的那些任务。从打印机的角度来看,打印队列实际上看起来可能是以下这个样子:

  • Lovecraft_-_Call_of_Cthulhu.txt

  • Doyle_-_The_Lost_World.txt

  • ERB_-_A Princess_of_Mars.txt

要查看某个打印机上的真实队列,就必须使用那台打印机设备随机附带的管理工具,这些超出了本书讨论的范围。

lpstat

lpq命令可以显示正在排队等待打印的文件,但它不能告诉你每个文件要发送到哪台打印机。要获取这一信息,需要使用6.1节中提到的lpstat命令。不过,这一次只使用lpstat命令,不需要任何选项:

$lpstat
bro-489    rsgranne 108544 Tue 10 Dec 2005
bro_wk-490 rsgranne 524288 Tue 10 Dec 2005

结果显示的是一列打印任务,在每一行的开始显示的是正在处理打印任务的打印机的名称。你曾经无意间将打印任务发送到当前没有连接上的打印机吗?用lpstat命令查找到这个打印任务,然后删除,下一节中将介绍如何具体操作。

lprm

你想取消发送到默认打印机上的当前打印任务吗?只需使用lprm(表示“lp remove”):

$ lprm

一定要迅速发送这个命令。现在的许多打印机速度都非常快,内存空间也充足,如果不快点,打印任务可能已经离开你的机器,正在打印机上运行了。在这种情况下,赶快找到打印机上的Cancel(取消)按钮,迅速按下。

lprm job ID

在上一节中,你学了如何取消发送到默认打印机上的当前打印任务。但是,打印任务已经在队列中,但好几分钟过去了还没有开始打印,怎么办?或者,如果打印任务正在发往某个打印机,但不是默认打印机,又该如何?在这些情况下,仍然可以使用lprm命令,但是需要通过引用打印任务的ID编号来告诉这个命令要取消哪个打印任务。

回头看看6.8节中的例子。第三列标记为“Job”,这一列的每一行都有一个数字。现在再看看6.9节。每个打印机名称的后面是一个连字符,后面跟着的那个数字与6.8节的例子中看到的相同。这个数字就是任务的ID编号。在lprm命令中指定这个编号,就可以精确地删除相应的打印任务。

$ lpstat
bro-489    rsgranne 108544 Tue 10 Dec 2005
bro_wk-490 rsgranne 524288 Tue 10 Dec 2005
$ lprm 490
$lpstat
bro-489    rsgranne 108544 Tue 10 Dec 2005

用这个特殊的命令,你真的可以不必再浪费打印纸、墨粉和墨水了。下一次当你意识到刚发送了一条把满是大图片的一份文档打印500份的命令到打印机时,就可以用lprm命令和任务ID编号来中止打印了。给我发封电子邮件,感谢一下我吧。

lprm -

如果你想取消多个打印任务,该怎么办?指定所有任务的ID编写,应该能够删除它们,如下所示:

$ lprm 489 490 491 492 493

不过,这样需要输入的内容就太多了。如果你想删除每台打印机队列中的所有的打印任务,只需要在lprm命令后面加上个连字符:

$ lprm -

这样输入命令真够快,没什么辛苦,这就是Linux“越懒越好”的传统。这没什么可感到惭愧的!

打印是人们在计算机上执行的一项重要活动,所以学会如何有效打印非常重要。作为打印过程的一部分,首先要知道如何查询打印机和打印任务,以及如何将这些任务发送到打印机。当然,并不是所有的任务都需要打印,所以知道如何取消不必要的打印任务也很重要。本章介绍了一些在Linux中使用打印机时涉及的主要操作任务,记住我的最后一条建议:能够在Linux中使用的自动双面打印机现在已经大幅降价,它们可以为你节省一部分打印纸的费用。以后要购买打印机时,就购买一台这样的设备,你将会为自己这么做而感到欣慰。



第7章 拥有者和权限

Linux从一开始就被设计成了多用户的操作系统(不像Windows,它是一个单用户的操作系统,甚至现在这还是Windows很多安全问题的根源)。多用户操作系统意味着不同的用户可以同时在系统中创建文件、删除目录以及读取不同的资源。为了防止用户之间的互相影响和对底层操作系统的破坏,就需要依靠一套已经创建好的权限系统。掌握了Linux权限管理的精髓,不管是个人使用的工作站,还是供多人访问的服务器,只要它是Linux计算机,你就可以大显身手。虽然工具简单,但是它们所提供的功能却一点儿也不简单。开始学习吧!

chgrp

几乎在每种Linux系统中,当你创建一个新的文件(或目录)时,默认的拥有者和用户组就是你自己。例如,假设你要新写一个能在系统上运行的脚本文件。

说明 为了节约篇幅,这里用省略号代替了很多使用ls –l命令通常能够看到的信息。

$ touch new_script.sh
$ ls -l
-rw-r--r-- 1 scott scott ... script.sh

说明 这台计算机中的用户名和用户组正好都是“scott”,但并不是每台计算机都必须这样。在创建文件时,用户的UID(User ID号)成为文件的拥有者,而用户的GID(Group ID号)则成为文件的用户组。

但如果你是系统中admins用户组中的一员,想让该组的其他成员也能够使用你的脚本,并能够运行它,该怎么办呢?在这种情况下,就需要使用chgrp命令将用户组从scott修改为admins

$ chgrp admins new_script.sh
$ ls -l
-rw-r--r-- 1 scott admins ... script.sh

说明 这个脚本现在仍然不能运行,因为它不是可执行文件。本章后面介绍chmod命令时,会详细介绍如何处理。

关于chgrp命令,需要记住两点。当运行chgrp命令时,可以使用用户组的名称或数字ID。但是如何找到与某个用户组相关的数字ID呢?最简单的方法就是对/etc/group使用cat命令,这个文件保存了计算机中各个用户组的信息,结果如下所示:

$ cat /etc/group
bind:x:118:
scott:x:1001:
admins:x:1002:scott,alice,bob
[list truncated for length]

关于chgrp命令的另外一点与安全有关:只有用户组的成员,才能够修改这个组的权限。换句话说,Scott、Alice或Bob可以使用chgrp命令让文件或者目录的用户组成为admins,但Carol不可以,因为她不是admins组的成员。

chgrp -R

当然,可能你不会只想修改一个文件或者目录的用户组。如果要修改目录中多个文件的用户组,可以使用通配符。如果要修改目录中所有文件及其所有子目录中文件的用户组,则可以使用-R(或--recursive)选项。

$ pwd
/home/scott/pictures/libby
$ ls -F
by_pool/  libby_arrowrock.jpg libby.jpg on_floor/
$ ls -lF *
-rw-r--r-- 1 scott scott ... libby_arrowrock.jpg
-rw-r--r-- 1 scott scott ... libby.jpg

by_pool/:
-rw-r--r-- 1 scott scott ... libby_by_pool_02.jpg
drwxr-xr-x 2 scott scott ... lieberman_pool

on_floor/:
-rw-r--r-- 1 scott scott ... libby_on_floor_01.jpg
-rw-r--r-- 1 scott scott ... libby_on_floor_02.jpg
$ chgrp -R family */*
$ ls -l *
-rw-r--r-- 1 scott family ... libby_arrowrock.jpg
-rw-r--r-- 1 scott family ... libby.jpg

by_pool:
-rw-r--r-- 1 scott family ... libby_by_pool_02.jpg
drwxr-xr-x 2 scott family ... lieberman_pool

on_floor:
-rw-r--r-- 1 scott family ... libby_on_floor_01.jpg
-rw-r--r-- 1 scott family ... libby_on_floor_02.jpg

警告 如果使用命令chgrp -R family*,并不会修改/home/scott/pictures/libby目录中的任何点(dot)文件。然而不能使用chgrp -R family .*命令。虽然它能够修改当前目录中的所有点文件,但.*也和..匹配,所以所有父目录中的文件也会被修改,或许这并不是你想要的结果。

chgrp –v

chgrp -c

或许你已注意到chgrp命令与所有行为良好的Linux应用程序一样,只有在出现问题时才提供反馈信息。如果程序运行正常,正确地完成了任务,它不会向你提示任何类似“喂!我完成了,一切正常!”这样的信息。只有当出现了需要解决的问题时,Linux命令行应用程序才会显示提示信息。

如果你想知道chgrp命令运行过程中发生了什么,可以先试试-v(或--verbose)选项。它可以告诉你chgrp命令在每一步中执行了什么任务。

$ ls -lF
drwxr-xr-x 4 scott scott ... by_pool/
-rw-r--r-- 1 scott scott ... libby_arrowrock.jpg
-rw-r--r-- 1 scott family ... libby.jpg
-rw-r--r-- 1 scott scott ... libby_on_couch.jpg
drwxr-xr-x 2 scott scott ... on_floor/
$ chgrp -v family *
changed group of 'by_pool' to family
changed group of 'libby_arrowrock.jpg' to family
group of 'libby.jpg' retained as family
changed group of 'libby_on_couch.jpg' to family
changed group of 'on_floor' to family
$ ls -lF
drwxr-xr-x 4 scott family ... by_pool/
-rw-r--r-- 1 scott family ... libby_arrowrock.jpg
-rw-r--r-- 1 scott family ... libby.jpg
-rw-r--r-- 1 scott family ... libby_on_couch.jpg
drwxr-xr-x 2 scott family ... on_floor/

注意,这个例子中发生的事情。libby.jpg文件已经成为family用户组的一员了,但是-v选项继续报告提示信息,确保用户知道libby.jpg的用户组已经是family。如果你正在修改某个用户组,可能不需要知道原来已经属于这个用户组的内容。在这种情况下,则需要使用-c(或--changes)选项,毫无疑问,它只会显示修改过的内容。

$ ls -lF
drwxr-xr-x 4 scott scott ... by_pool/
-rw-r--r-- 1 scott scott ... libby_arrowrock.jpg
-rw-r--r-- 1 scott family ... libby.jpg
-rw-r--r-- 1 scott scott ... libby_on_couch.jpg
drwxr-xr-x 2 scott scott ... on_floor/
$ chgrp -c family *
changed group of 'by_pool' to family
changed group of 'libby_arrowrock.jpg' to family
changed group of 'libby_on_couch.jpg' to family
changed group of 'on_floor' to family
$ ls -lF
drwxr-xr-x 4 scott family ... by_pool/
-rw-r--r-- 1 scott family ... libby_arrowrock.jpg
-rw-r--r-- 1 scott family ... libby.jpg
-rw-r--r-- 1 scott family ... libby_on_couch.jpg
drwxr-xr-x 2 scott family ... on_floor/

这一次不会显示关于libby.jpg的信息,因为它早已属于family用户组了。所以,如果你想得到完整的显示,甚至包括没有修改过的文件,就应该使用-v选项;如果你想得到简洁点儿的显示,就应该使用-c选项。

chown

修改文件的用户组固然重要,但是修改文件的拥有者可能会更常用。修改用户组使用的是chgrp命令,而要修改拥有者则应该使用chown命令。

$ ls -l
-rw-r--r-- 1 scott scott ... libby_arrowrock.jpg
-rw-r--r-- 1 scott family ... libby.jpg
-rw-r--r-- 1 scott scott ... libby_on_couch.jpg
$ chown denise libby.jpg
$ ls -l
-rw-r--r-- 1 scott scott ... libby_arrowrock.jpg
-rw-r--r-- 1 denise family ... libby.jpg
-rw-r--r-- 1 scott scott ... libby_on_couch.jpg

在7.1节中提到的关于chgrp命令的几点内容也适用于chown命令。chgrp命令使用的是用户名或其数字ID。用户的数字ID可以通过运行cat/etc/passwd命令来查看,如下所示:

bind:x:110:118::/var/cache/bind:/bin/false
scott:x:1001:1001:Scott,,,:/home/scott:/bin/bash
ntop:x:120:120::/var/lib/ntop:/bin/false

第一个数字是用户的数字ID(第二个数字是与用户相关联的主用户组的数字ID)。

同样,只有文件的当前拥有者才能修改其拥有者属性(当然,root也能修改)。这并不让人吃惊,但是最好还是记住。

警告 使用chown -R scott *,并不会修改目录中的任何点文件。然而,不应该使用chown -R scott .*。它能够修改当前目录中的所有点文件,但.*也和..匹配,所有父目录中的文件也会被修改,或许这并不是你想要的结果。

chown owner:group

前面已经看到,可以用chgrp命令修改用户组,用chown命令修改拥有者,但是用chown命令也可以同时实现这两个功能。在chown的后面先指定用户,再指定用户组,中间用冒号(:)隔开,最后是要修改的文件或目录(这就是为什么要避免在用户或用户组名称中使用冒号的原因之一)。

$ ls -l
-rw-r--r-- 1 scott scott ... libby.jpg
$ chown denise:family libby.jpg
$ ls –l
-rw-r--r-- 1 denise family ... libby.jpg

甚至可以只用chown命令来修改用户组,省略冒号前面的用户名就可以。

$ ls -l
-rw-r--r-- 1 scott scott ... libby.jpg
$ chown :family libby.jpg
$ ls –l
-rw-r--r-- 1 scott family ... libby.jpg

提示 如果用户或用户组的名称中有冒号怎么办?只要在冒号前面输入反斜杠字符(\)就可以了,它能够“转义(escape)”字符,告诉系统这只是个冒号,并不是用户和用户组名称之间的分隔符。

  $ chown denise:family\:parents libby.jpg

这样做虽然有效,但是最好不要在用户和用户组名称中使用冒号。

因为chown能够完成chgrp的所有功能,所以几乎不需要使用chgrp命令,除非你喜欢。

说明 在分隔用户和用户组时,可以使用点号(.)或冒号(:)字符。但是因为点号(.)的用法已经被废弃了,所以新的建议还是推荐继续使用冒号(:)。

chmod命令可以修改与文件或目录相关联的权限,在学习这个命令之前,回顾一下Linux是如何理解这些权限的。

说明 Linux系统正在开始使用一种控制粒度更细、功能也更强大的权限系统,名为ACL(Access Control List,访问控制列表)。不过目前ACL仍未得到广泛使用,所以这里没有介绍它。有关ACL的更多信息,可以查看Linux Magazine的“Access Control Lists”(www.linux-mag.com/2004-11/guru_01.html),以及Open Source Weblog上的“An ACL GUI for Linux”(http://opensource.weblogsinc.com/2005/12/06/an-acl-gui-for-linux/)。

Linux认为有三组用户会使用文件或目录,即实际拥有者(也称为文件的用户)、用户组及系统中的其他人。每组用户分别用不同的字母表示,如表7-1所示。

表7-1 用户组及其缩写
用 户 组 缩  写
用户(拥有者)(User) u
组(Group) g
其他(Other) o

在2.10节中,已经介绍过如何查看完整的权限信息,看看什么用户能够对文件和目录进行哪些操作。2.10节介绍了三种属性:读、写和执行,分别表示为rwx。其他可能的权限还有suidsgid和sticky bit,分别表示为s(在有些系统中为S)、s(或S)和t(或T)。但是要牢记,根据使用这些权限的是文件还是目录,所有这些权限字符的意义可能会有所不同。表7-2总结了每种权限属性、缩写以及它们各自的含义。

表7-2 权限字母及其含义
文件属性 缩写 对文件的含义 对目录的含义
可读

(readable)
r 可以查看 可以使用ls命令列出其内容
可写

(writable)
w 可以编辑 可以删除、重命名或添加文件
可执行

(executable)
x 可以作为程序来运行 可以读取它的文件和子目录,或运行文件
suid s 任何用户都可以使用拥有者的权限来执行文件 不可用
sgid s 任何用户都可以使用用户组的权限来执行文件 在目录中新创建的任何文件都属于拥有该目录的用户组
sticky bit t 告诉OS经常要执行这个文件,所以通常把它保存在交换区以便快速访问(只适用于较早的Unix系统,Linux将忽略这个属性) 用户不能删除或重命名文件,除非当前用户是文件或目录的拥有者

说明 root用户一直能够对任何文件或目录进行任何操作,所以表7-2中的限制并不适用于root。

在接下来的几节中会详细介绍表7-2中的每个文件属性。现在你应该已经理解这些权限了,接下来就看看如何用chmod命令修改文件和目录的权限。

chmod [ugo][+-=][rwx]

chmod命令使用两种权限表示法:字母或数字。二者各有各的优点,但是有时对于用户来说,先学习字母系统要更容易些。字母表示法基本上使用一个简单的公式:想要修改的用户组(u、g、或者o),后面用加号(+)来授予权限、用减号(-)来删除权限、用等号(=)来设置完全匹配的权限,后面再跟上表示想要修改的权限的字母(r、w、x、s、或者t)。例如,假设你想让family用户组的成员能够修改图片。

$ ls –l
-rw-r--r-- 1 scott family ... libby.jpg
$ chmod g+w libby.jpg
$ ls -l
-rw-rw-r-- 1 scott family ... libby.jpg

非常简单。但是如果你想让family组以及所有其他用户都有权改写文件,该怎么办呢?

$ ls –l
-rw-r--r-- 1 scott family ... libby.jpg
$ chmod go+w libby.jpg
$ ls -l
-rw-rw-rw- 1 scott family ... libby.jpg

当然,因为你真正想要的是让所有用户(拥有者、用户组以及所有其他人)都能读和写访问,应该使用以下命令:

$ ls –l
-rw-r--r-- 1 scott family ... libby.jpg
$ chmod a=rw libby.jpg
$ ls -l
-rw-rw-rw- 1 scott family ... libby.jpg

如果你觉得自己犯了一个错误,需要删除family组和其他人的权限,不让他们修改图片,甚至要确保其他人都看不到图片。

$ ls –l
-rw-rw-rw- 1 scott family ... libby.jpg
$ chmod go-w libby.jpg
$ ls -l
-rw-r--r-- 1 scott family ... libby.jpg
$ chmod o-r libby.jpg
$ ls -l
-rw-r----- 1 scott family ... libby.jpg

除了使用-,也可以使用=:

$ ls –l
-rw-rw-rw- 1 scott family ... libby.jpg
$ chmod g=r libby.jpg
$ ls -l
-rw-r--rw- 1 scott family ... libby.jpg
$ chmod o= libby.jpg
$ ls -l
-rw-r----- 1 scott family ... libby.jpg

注意最后的chmod命令,o等于什么也没有,相当于删除了系统中所有其他用户的所有权限。现在就又快又有效率了。

字母系统的优点在于它的速度通常很快。但是从最后一个例子中也能看到它的主要缺点:如果要修改两个或更多的用户组,每个用户组的修改又各不相同,至少要运行chmod命令两次。下一节就将介绍如何用数字表示法来解决这个问题。

chmod [0-7][0-7][0-7]

数字权限(也称为八进制权限)是基于二进制数字系统而创建的。我们将不解释为什么权限要用特定的数字来表示,而重点介绍最终的结果:读(read,r)的值为4,写(write,w)的值为2,而执行(execute,x)的值是1。记住,Linux权限识别三个用户组,即拥有者、组以及其他人,每个用户组都能进行读、写和执行操作(如表7-3所示)。

表7-3 权限和相应的数字表示
Owner(拥有者) Group(组) World(其他人)
权限 r; w; x r; w; x r; w; x
数字表示 4; 2; 1 4; 2; 1 4; 2; 1

在这种模式下,权限组合就演变成了简单的加法运算。下面举一些例子。

  • 用户具有读和写文件或目录的权限。读是4,写是2,执行是0(因为没有授权),4 + 2 + 0 = 6。
  • 用户具有读和执行文件的权限。读是4,写是0(因为没有授权),执行是1,4 + 0 + 1 = 5。
  • 用户具有读、写及执行目录的权限。读是4,写是2,执是1,4 + 2 + 1 = 7。

使用这种方法,用户组的最大权限值是7(读、写和执行),最小权限值是0(不能读、写和执行)。因为有三种用户组,所以就有三个相应的数字,每个值都介于0和7之间,分别表示相应用户组的权限。表7-4展示了可能的权限数值以及它们各自的含义。

表7-4 ls -l命令表示的数字权限
数  字 ls -l命令的表示 数  字 ls -l命令的表示
0 --- 4 r--
1 --x 5 r-x
2 -w- 6 rw-
3 -wx 7 rwx

虽然可以设置各种各样的权限,但是有一些权限会经常不断出现。表7-5列举了几种常用的权限以及它们的含义。

表7-5 使用ls -l命令表示的通用权限
Chmod命令 ls -l表示 含  义
chmod 400-r--------拥有者能够读,其他任何人不能进行任何操作
chmod 644-rw-r--r--所有人都能够读,但只有拥有者才能编辑
chmod 660-rw-rw----拥有者和组用户能够读和写,其他人不能进行任何操作
chmod 664-rw-rw-r--所有人都能读,但只有拥有者和组用户能够编辑
chmod 700-rwx------拥有者能够读、写和执行,其他用户不能进行任何操作
chmod 744-rwxr--r--所有人都能读,但只有拥有者才能编辑和执行
chmod 755-rwxr-xr-x所有人都能读和执行,但只有拥有者才能编辑
chmod 777-rwxrwxrwx所有人都能读、写和执行(这样的设置通常不是个好想法)

警告 可以对文件或目录使用chmod 000,但是那样的话,唯一能够对它进行任何操作或再使用chmod命令修改权限的用户就只有root了。

现在应该了解八进制权限了吧。与权限的字母表示法相比,八进制权限的理解需要更多的思考,但是它的优点就是能够一次设置许多权限。再看一下7.8节中的例子,这里演示如何用权限的数字表示法来完成相同的功能。

假设你想允许family组的成员能够修改图片。

$ ls –l
-rw-r--r-- 1 scott family ... libby.jpg
$ chmod 664 libby.jpg
$ ls -l
-rw-rw-r-- 1 scott family ... libby.jpg

假设你想让family组以及所有其他用户具有写这个文件的权限,应该怎么办?

$ ls –l
-rw-r--r-- 1 scott family ... libby.jpg
$ chmod 666 libby.jpg
$ ls -l
-rw-rw-rw- 1 scott family ... libby.jpg

如果你觉得自己犯了个错误,需要删除family组和其他人的权限,不让他们修改图片,甚至要确保其他人都看不到图片。

$ ls –l
-rw-rw-rw- 1 scott family ... libby.jpg
$ chmod 640 libby.jpg
$ ls -l
-rw-r----- 1 scott family ... libby.jpg

这个例子体现了权限的数字表示法的重要优点。使用权限的字母表示法需要两步,先是chmod go-w,然后是chmod o-r(或chmod g=r,然后chmod o=),而使用权限的数字表示法则只需要一条简单的命令就可以了。也正因为这个原因,经常会看到高深的Linux大师使用权限的八进制表示法,因为它更快捷而且看起来更精确。

chmod -R

到目前为止,或许你已经注意到很多Linux命令可以递归地应用于文件和目录,chmod命令也不例外。使用-R(或--recursive)选项能够在短时间内修改数百个文件系统对象的权限,只是要确保你确实想要这么做。

$ pwd
/home/scott/pictures/libby

$ ls -lF
drwxrw---- 2 scott scott ... by_pool/
-rw-r--r-- 1 scott scott ... libby_arrowrock.jpg
-rw-r--r-- 1 scott scott ... libby.jpg
drwxrw---- 2 scott scott ... on_floor/
$ ls -l *
-rw-r--r-- 1 scott scott ... libby_arrowrock.jpg
-rw-r--r-- 1 scott scott ... libby.jpg

by_pool:
-rw-r--r-- 1 scott scott ... libby_by_pool_02.jpg
-rwxr-xr-x 2 scott scott ... lieberman_pool.jpg

on_floor:
-rw-r--r-- 1 scott scott ... libby_on_floor_01.jpg
-rw-r--r-- 1 scott scott ... libby_on_floor_02.jpg
$ chgrp -R family *
$ chmod -R 660 *
chmod: 'by_pool': Permission denied
chmod: 'on_floor': Permission denied

Permissin denied?”这是怎么了?看看表7-2。如果文件是可执行的,那么它就可以作为程序来运行;但目录也必须是可执行的,才能允许用户访问其内部,读取它的文件和子目录。上面运行的chmod -R 660 *命令删除了所有内容(包括文件和目录)的x权限。当chmod命令报告它已经完成执行时,因为它不能读取目录中的内容(这些目录不再是可执行的),所以不能成功完成操作。

那应该怎么办呢?还真没有一个简单的答案。可以使用通配符来运行chmod命令,让它只修改特定类型的文件,如下所示:

$ chmod -R 660 *.jpg

这个命令修改的只是图像,而不是目录,所以不会有任何问题。但是如果文件类型不止一种,这样的操作很快就会变得乏味,不得不为每种文件都运行一次chmod命令。

如果子目录里面还有很多子目录,或者需要处理的文件太多,使用find命令会更好。用find命令先找出所有文件(不是目录),然后再修改它们的权限。第10章将详细介绍这个命令的使用方法。

需要特别注意的是,当递归地修改权限时,一定要小心。或许你得到的结果并不是自己原来想要的,偶尔还会造成无法访问某些文件或子目录。

chmod u[+-]s

在7.6节中介绍了几种可能的权限。主要介绍的是rwx,因为它们是最常用的几种权限,不过其他权限迟早也会遇到。我们先来看看suid,它只适用于可执行文件,不能用于目录。

在设置suid以后,用户就可以使用拥有者的权限执行这个文件了,就好像是程序的拥有者在运行它一样。使用suid的一个常见例子是passwd命令的权限设置,这个命令可以让用户设置和修改他们的密码。

$ ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root ... /usr/bin/passwd

可以看到passwd设置了suid权限,因为在用户的x权限位置,现在有一个s权限。passwd的拥有者是root用户,但是有必要让普通用户也能运行这个命令,否则他们就不能修改自己的密码。为了让所有人都能执行passwd命令,需要为用户、组及系统的其他用户都设置x权限。不过,这还不够。答案是将passwd设成suid root,这样所有人都能以root用户的权限运行这个命令。

说明 或许你已经看到用sS都可以表示设置了suid。在设置suid之前,如果拥有者已经具有执行权限(x),就会看到s;而S则表示在设置suid之前,拥有者没有执行的权限。最终的结果是一样的,但是字母的大小写能够告诉你最初的情形是什么样的。

有两种办法可以用于设置和取消suid,即字母表示法和数字表示法。字母表示法如下所示:

$ pwd
/home/scott/bin
$ ls -l
-rwxr-xr-- 1 scott admins ... backup_data
$ chmod u+s backup_data
$ ls -l
-rwsr-xr-- 1 scott admins ... backup_data

现在admins组的任何成员都能够运行backup_data脚本,好像他们就是用户scott一样。但是要注意,任何非admins组的成员都不能运行这个程序,因为他们只有程序的读权限。如果有必要让系统中的每个用户都能够作为scott来运行backup_data脚本,那么就应该把权限设置成-rwsr-xr-x

使用u-来代替u+即可删除suid

$ ls -l
-rwsr-xr-- 1 scott admins ... backup_data
$ chmod u-s backup_data
$ ls -l
-rwxr-xr-- 1 scott admins ... backup_data

使用八进制权限表示法设置suid要更复杂些,这是因为它在原来使用的数字权限表示法中引入了新的一位数字。回想一下,数字权限表示法使用了3个数字,第一个数字表示拥有者的权限,第二个表示组用户的权限,第三个则表示其他人的权限。其实在拥有者数字的左边还有第四个数字。只不过在绝大多数情况下,这个数字为0,所以没有必要显示或使用它。换句话说,chmod 644 libby.jpgchmod 0644 libby.jpg是完全相同的。只有在修改suid(或sgid、sticky bit,随后几节将详细介绍)时,才需要第四位数字。

要将suid的数字设置为4,可以按如下所示的方法来修改backup_data

$ pwd
/home/scott/bin
$ ls -l
-rwxr-xr-- 1 scott admins ... backup_data
$ chmod 4754 backup_data
$ ls -l
-rwsr-xr-- 1 scott admins ... backup_data

要删除suid,只要将这一位数字设置为0,就能将权限设置回默认状态,不设置suid权限。

$ ls -l
-rwsr-xr-- 1 scott admins ... backup_data
$ chmod 0754 backup_data
$ ls -l
-rwxr-xr-- 1 scott admins ... backup_data

说明 作为普通用户,一般很少需要将程序的权限修改为suid。它的使用通常都与root用户拥有的程序有关,但是对此有所了解仍然是件好事情,以备在偶尔需要时能派上用场。

chmod g[+-]s

suid密切相关的是sgidsgid权限既适用于文件,也适用于目录。对于文件,sgidsuid的功能类似,只不过用户是以组的权限执行文件,而不是以拥有者的权限执行文件。例如,系统中的crontab命令就可能设置为sgid,以便用户就能够让cron为他们运行程序。不过,是以更加受限的crontab组,而不是以无所不能的root用户来运行程序。

$ ls -l /usr/bin/crontab
-rwxr-sr-x 1 root crontab ... /usr/bin/crontab

对于目录,sgid则会做些有趣的事情:任何后来在该目录中创建的文件都属于分配给该目录的组。下面的示例会将这些阐述得更明白。

假设有三个用户,Alice、Bob和Carol,他们都是admins组的成员。Alice的用户名是alice,她的主要组也是alice,这在大多数Linux系统中很常见。Bob和Carol也遵循同样的模式,他们的用户名和主要组分别是bobcarol。如果Alice在admins组共享的目录中创建了一个文件,那么这个文件的拥有者和用户组就是alice,这意味着admins组的其他成员将不能写该文件。然而,在Alice创建新文件以后,她可以运行chgrp admins document(或chown :admins document)修改权限,但是这样很快就会变得相当烦琐。

不过,如果将共享目录设置为sgid,那么任何在该目录中创建的新文件的拥有者仍然是创建该文件的用户,但其用户组会自动地分配为共享目录的用户组。在这个例子中,为admins组。这样,Alice、Bob和Carol都能够读取和编辑在共享目录中创建的任何文件,而且实现的途径也最简捷。

同样,可以使用字母表示法或数字表示法设置sgid。使用字母时,设置sgid与设置suid类似,只是这里使用的是g,而不是u。将sgid应用于目录,如下所示。(不过记住当用于文件时,其处理过程也是一样的。)

$ ls -lF
drwxr-xr-x 11 scott admins ... bin/
$ chmod g+s bin
$ ls -lF
drwxr-Sr-x 11 scott admins ... bin/

说明 或许你可能看到在表示设置了sgid权限时既使用了s,也使用了S。在设置sgid之前,如果用户组已经具有了执行权限(x),结果就会看到s;而S表示在设置sgid之前,还没有为用户组设置执行的权限。最终的结果是一样的,但是字母的大小写能够告诉你最初的情形是什么样的。

删除sgid权限的过程差不多就是与添加相反的过程。

$ ls -lF
drwxr-Sr-x 11 scott admins ... bin/
$ chmod g-s bin
$ ls -lF
drwxr-xr-x 11 scott admins ... bin/

如果你还没有阅读7.11节,那么可以回去看看,因为7.11节介绍了在表示拥有者权限的数字之前,另外出现那个神秘的第四位数字的来龙去脉。对于suid,那个数字是4;对于sgid,这个数字是2。

$ ls -lF
drwxr-xr-x 11 scott admins ... bin/
$ chmod 2755 bin
$ ls -lF
drwxr-Sr-x 11 scott admins ... bin/

删除sgid的方法和删除suid的方法是一样的,在开头输入0,就去掉了sgid权限。

$ ls -lF
drwxr-Sr-x 11 scott admins ... bin/
$ chmod 0755 bin
$ ls -lF
drwxr-xr-x 11 scott admins ... bin/

说明 你已经知道在sgid目录中创建新文件将会发生什么,不过,也要小心sgid对其他的文件系统处理的影响。如果使用cp命令将文件复制到sgid目录,新复制过来的文件能够获得目录的用户组。但是如果用mv命令将文件移动到sgid目录,文件将仍然保持它当前所属的用户组,并不会获得目录的用户组。最后,如果用mkdir命令在sgid目录中创建新目录,它不仅能够继承sgid目录所属于的用户组,其本身也将拥有sgid权限。

chmod [+-]t

除了是一个可以脱口而出的有趣短语以外,sticky bit还有什么意思呢? 在以前的Unix中,如果可执行文件设置了sticky bit,OS就会知道这个文件将需要不断地运行,所以就把它保存在交换区,以便能够快速而有效地访问它。Linux是一个较新的系统,它会忽略在文件上设置的sticky bit权限。

这意味着sticky bit只能作用于目录。在文件夹设置了sticky bit以后,除了文件的拥有者或设置了sticky bit权限的目录的拥有者以外,其他用户都不能删除或重命名该目录中的文件。如果文件夹没有设置sticky bit权限,而且该文件夹对于用户来说是可写的,那么这些用户就可以删除或重命名这个目录中的任何文件。sticky bit权限就是用于防止这种情况发生的。这一权限最常用于/tmp目录,通常是将这个目录设计为允许所有人写入,但是/tmp目录中的单独文件和文件夹要通过sticky bit权限加以保护,以免其他用户破坏。

$ ls -l /
drwxrwxrwt 12 root root ... tmp
[Results truncated for length]

说明 或许你可能看到在表示设置了sticky bit权限时既使用了t,也使用了T。在设置sticky bit之前,如果所有人(the world)已经具有了执行权限(x),结果就会看到t;而T表示在设置sticky bit之前,还没有为所有人(the world)设置执行的权限。最终的结果是一样的,但是大小写的区别能够告诉你最初的情况是什么样的。

与本章中很多使用chmod命令的例子一样,也可以使用字母表示法或者数字表示法设置sticky bit。

$ ls -lF
drwxrwxr-x 2 scott family ... libby_pix/
$ chmod +t libby_pix
$ ls -lF
drwxrwxr-t 2 scott family ... libby_pix/

这里有两个地方容易让人混淆。首先,虽然前面的例子在用字母表示法设置权限时需要明确指定操作将会影响到谁(例如,必须输入一个ug或者o),但是在设置sticky bit时则没有这个必要,只输入+t就可以了。

其次,注意出现在所有人(the world)的执行权限位置处的t,即使目录并不是所有人都可写的,它也仍然允许family组的成员能够对该目录进行写操作,同时也会阻止该组的成员删除文件,除非他们是文件的拥有者。

删除sticky bit的操作应该与你想象的一样直接。

$ ls -lF
drwxrwxr-t 2 scott family ... libby_pix/
$ chmod -t libby_pix
$ ls -lF
drwxrwxr-x 2 scott family ... libby_pix/

使用八进制权限表示法设置sticky bit也会涉及7.10节和7.11节中介绍过的第四位数字。suid使用的是4,sgid使用的是2,而sticky bit则使用1。(看出规律了吗?)

$ ls -lF
drwxrwxr-x 2 scott family ... libby_pix/
$ chmod 1775 libby_pix
$ ls -lF
drwxrwxr-t 2 scott family ... libby_pix/

同样,将权限位置为0可以取消sticky bit。

$ ls -lF
drwxrwxr-t 2 scott family ... libby_pix/
$ chmod 0775 libby_pix
$ ls -lF
drwxrwxr-x 2 scott family ... libby_pix/

在工作站(workstation)的很多目录上,sticky bit权限并不经常使用,但是在服务器上用起来相当方便。记住这个命令吧,你将发现它还能够解决其他一些棘手的权限问题。

提示 为了加快操作命令行的速度,可以同时设置suidsgid和sticky bit权限的组合。与将4(读)、2(写)和1(执行)相加获得用户的数字权限一样,也可以对suidsgid和sticky bit做同样的处理。如表7-6所示。

表 7-6
数  字 含  义
0 删除sticky bit、sgidsuid
1 设置sticky bit
2 设置sgid
3 设置sticky bit和sgid
4 设置suid
5 设置sticky bit和suid
6 设置sgidsuid
7 设置sticky bit、sgidsuid

一定要注意:用0会同时删除suidsgid和sticky bit权限。如果用0删除suid,但仍然想设置sticky bit,就必须回去再重新设置sticky bit。

权限对于Linux系统的安全性、甚至健全性(sanity)都至关重要,但要精通权限管理似乎并非易事。不过,经过一番学习和思考以后,就有可能很好地处理Linux权限,并让它们为你服务。chgrp命令能够修改组权限,chown命令能够修改用户权限(包括组权限),而功能强大的chmod命令则为Linux用户提供了丰富的工具,把这些命令组合起来就可以强力而有效地设置权限。



第8章 归档和压缩

虽然在闲谈时,归档和压缩的区别有时比较模糊,但实际上对文件进行归档和压缩是完全不同的。归档可以是把10个文件组合到一个文件中,在大小上没有什么不同。如果你有10个100 KB的文件,对它们进行归档,最终得到的是一个1000 KB的文件。而对这10个文件进行压缩,最终得到的文件的大小可能相差很多,从几千字节(KB)到接近原来大小的100 KB,这得由原始文件的类型来决定。

说明 事实上,有可能在压缩过程中得到的文件的体积会更大!如果文件已经压缩过,那么再次压缩就会增加一些额外的开销,导致生成的文件会稍微大些。

本章提到的所有归档和压缩文件格式(如zipgzipbzip2以及tar)都很流行,而zip可能是世界上最广泛使用的格式。这是因为它在Windows中几乎是通用的,而且差不多所有主要的(和最次要的)操作系统都可以很好地支持zipunzip,所以用zip压缩的文件在Linux和Mac OS上也能正常使用。如果要把归档文件发送给其他用户,但是不知道他们会使用什么操作系统,zip格式就是安全的选择。

gzip是一个开源程序,用于取代旧的Unix程序compress。几乎在世界上所有基于Unix的系统中都能够找到它,包括Linux和Mac OS X,但在Windows中比较少见。如果你在和基于Unix计算机的用户来回发送文件,gzip格式将是安全的选择。

bzip2命令是Linux系统中的后起之秀,主要用于取代gzipbzip2创建的文件体积更小,但需要以牺牲速度作为代价。不过,目前计算机的速度如此之快,以致于大多数用户都不会注意到gzipbzip2在压缩一组文件时使用的时间有多大差别。

说明 Linux Magazine发表过一篇对几种不同的压缩格式进行比较的文章,网址为http://www.linux-mag.com/content/view/1678/43/

zipgzipbzip2主要用于压缩(虽然zip也可以用于归档);而tar命令则只有一个功能,就是归档,而且长期以来人们一直这么用它。tar几乎完全应用于基于Unix的计算机。如果你下载的是程序源代码,肯定将会遇到tar文件(也称为tar ball),而且几乎每一个Linux用户在他的生涯中都可能遇到tar ball。

zip

zip既可以对文件进行归档,也可以对文件进行压缩,这样有利于将多个文件作为电子邮件的附件发送、备份文件,或节约磁盘空间。使用zip很简单。假设你想通过邮件把一个TIFF文件发送给某个人,TIFF图像没有经过压缩,它的体积可能非常大。压缩它,应该可以让电子邮件的附件变得小一些。

说明 在使用ls -l命令时,只显示对每个例子来说必要的信息。

$ ls -lh
-rw-r--r-- scott scott 1006K young_edgar_scott.tif
$ zip grandpa.zip young_edgar_scott.tif
  adding: young_edgar_scott.tif (deflated 19%)
$ ls -lh
-rw-r--r-- scott scott 1006K young_edgar_scott.tif
-rw-r--r-- scott scott 819K grandpa.zip
➥grandpa.zip

在这个例子中,正如zip命令显示的信息所示,生成的zip文件的体积缩减了大约200 KB(或缩小了19%)。这样做很棒!而且还可以一次压缩多个图像。

$ ls -l
-rw-r--r-- scott scott 251980 edgar_intl_shoe.tif
-rw-r--r-- scott scott 1130922 edgar_baby.tif
-rw-r--r-- scott scott 1029224 young_edgar_scott.tif
$ zip grandpa.zip edgar_intl_shoe.tif edgar_
➥baby.tif young_edgar_scott.tif
  adding: edgar_intl_shoe.tif (deflated 4%) 
  adding: edgar_baby.tif (deflated 12%)
  adding: young_edgar_scott.tif (deflated 19%)
$ ls -l
-rw-r--r-- scott scott 251980 edgar_intl_shoe.tif
-rw-r--r-- scott scott 1130922 edgar_baby.tif
-rw-r--r-- scott scott 2074296 grandpa.zip
-rw-r--r-- scott scott 1029224 young_edgar_scott.tif

不过,用这种方法压缩单独的文件并不是很优雅。压缩3个文件时,这种方法还不算糟糕。收到压缩文件的人解压grandpa.zip文件,最后得到3个单独的文件。但是如果一个压缩文件包含了50个文件,用户解压后得到的文件将放置得很零乱。最好是压缩包含这50个文件的目录,这样用户解压以后,得到的将是一个清爽的目录。

$ ls -lF
drwxr-xr-x scott scott edgar_scott/
$ zip grandpa.zip edgar_scott
adding: edgar_scott/ (stored 0%)
adding: edgar_scott/edgar_baby.tif (deflated 12%)
adding: edgar_scott/young_edgar_scott.tif (deflated 19%)
adding: edgar_scott/edgar_intl_shoe.tif (deflated 4%)
$ ls -lF
drwxr-xr-x scott scott     160 edgar_scott/
-rw-r--r-- scott scott 2074502 grandpa.zip

不管压缩一个文件、多个文件,还是目录,用法模式都是相同的:zip命令,后面跟着想要创建的Zip文件的名称,最后是想要加到Zip文件的项目内容。

-[0-9]

zip进行压缩时,可以调整其压缩率。zip命令使用09来表示压缩率的度量标准:0表示“一点也不压缩”(和tar相似,稍后会看到),1表示“尽快完成压缩,而不需要压缩太多”,9表示“尽可能压缩文件,不介意会为此等待较长的时间”。默认值是6,但现在的计算机速度已经足够快,一直使用9表示的压缩率也不会有什么问题。

假设你有兴趣研究Herman Melville的Moby-Dick,想收集一些关键的文字资料来帮助你理解这本书,如Moby-Dick、Milton的Paradise Lost。我们比较一下使用不同压缩率的结果。

$ ls -l
-rw-r--r-- scott scott 102519 job.txt
-rw-r--r-- scott scott 1236574 moby-dick.txt
-rw-r--r-- scott scott 508925 paradise_lost.txt
$ zip -0 moby.zip *.txt
adding: job.txt (stored 0%)
adding: moby-dick.txt (stored 0%)
adding: paradise_lost.txt (stored 0%)
$ ls -l
-rw-r--r-- scott scott 102519 job.txt
-rw-r--r-- scott scott 1236574 moby-dick.txt
-rw-r--r-- scott scott 1848444 moby.zip
-rw-r--r-- scott scott 508925 paradise_lost.txt
$ zip -1 moby.zip *txt
updating: job.txt (deflated 58%)
updating: moby-dick.txt (deflated 54%)
updating: paradise_lost.txt (deflated 50%)
$ ls -l
-rw-r--r-- scott scott 102519 job.txt
-rw-r--r-- scott scott 1236574 moby-dick.txt
-rw-r--r-- scott scott 869946 moby.zip
-rw-r--r-- scott scott 508925 paradise_lost.txt
$ zip -9 moby.zip *txt
updating: job.txt (deflated 65%)
updating: moby-dick.txt (deflated 61%)
updating: paradise_lost.txt (deflated 56%)
$ ls -l
-rw-r--r-- scott scott 102519 job.txt
-rw-r--r-- scott scott 1236574 moby-dick.txt
-rw-r--r-- scott scott 747730 moby.zip
-rw-r--r-- scott scott 508925 paradise_lost.txt

比较结果如表8-1所示。

表 8-1
图  书 zip -0 zip -1 zip -9
Moby-Dick 0% 54% 61%
Paradise Lost 0% 50% 56%
Job 0% 58% 65%
合计(以B为单位) 1848444 869946 747730

从以上比较可以看出,压缩结果会随文件类型(文本文件的压缩效果通常比较好)和原始文件的大小而有所不同,让你对压缩效果有个大致的了解。除非你的计算机的速度真的很慢,或者确实没有耐心,否则的话可以一直使用-9,获得最大的压缩率。

说明 如果想要更灵活些,可以在.bashrc文件中为zip命令定义一个别名,如下所示:

  alias zip='zip -9'

这样一来,就能一直使用-9的压缩率,而不用再刻意考虑它了。

-P

-e

Zip程序允许使用-P选项对生成的Zip文档增加密码保护。但是不应该使用这个选项,因为它一点儿也不安全,如下所示(实际密码是12345678):

$ zip -P 12345678 moby.zip *.txt

因为是在命令行中直接指定密码,所以任何查看shell历史的人都能在列表中看到你在此输入的密码(其他人可以非常容易地做到这件事,这或许会让你感到很吃惊吧)。所以千万不要使用-P选项!

实际上应该使用的是-e选项,它能够对zip文件的内容进行加密,并设置密码。这两个选项的区别是,-e选项会提示你输入密码,这样密码就不会保存在shell事件的历史记录中了。

$ zip -e moby.zip *.txt
Enter password:
Verify password:
adding: job.txt (deflated 65%)
adding: moby-dick.txt (deflated 61%)
adding: paradise_lost.txt (deflated 56%)

使用这个选项时,唯一在shell事件历史中保存的部分只是zip -e moby.zip *.txt。真正输入的密码消失得无影无踪,任何查看shell历史记录的人都看不到。

警告 zip程序的密码保护所能提供的安全性并不算好。事实上,在因特网上可以很容易地找到很多工具,快速破解用密码保护的zip文档。可以把加了密码保护的zip文件想象成是在明信片上写的信息,再把它密封在信封中:对普通人来说,这种保护措施已经足够了;但这并不能阻止处心积虑的黑客。

此外,有些Linux发行版本中的zip还可能不支持加密。在这种情况下,将出现一个zip 错误:“encryption not supported(不支持加密)”。唯一的解决办法是:重新编译zip的源码。唉,真不幸!

unzip

解压zip文档一点儿也不难。创建zip压缩文档使用的是zip命令,而解压这些压缩文档使用的则是unzip命令。

$ unzip moby.zip
Archive: moby.zip
inflating: job.txt
inflating: moby-dick.txt
inflating: paradise_lost.txt

unzip命令可以提供运行时的情况。要获得更多的信息,可以加上-v选项(当然,该选项表示的还是verbose)。

 unzip -v moby.zip
Archive:  moby.zip
Length  Method  Size  Ratio  CRC-32  Name
------- ------ ------ ----- ------ ----
 102519 Defl:X  35747  65%  fabf86c9 job.txt
1236574 Defl:X 487553  61%  34a8cc3a moby-dick.txt
 508925 Defl:X 224004  56%  6abe1d0f paradise_lost.t
-------        ------  ---           -------
1848018        747304  60%           3 files

以上显示的信息有一些有用的数据,包括用于压缩文件的方法、压缩文件原来的压缩率以及用于错误纠正的循环冗余校验码 (CRC)。

-l

有时你可能看着某个zip文件,但不记得里面包含了什么文件。又或许你想确认一下zip文件里面是否包含自己需要的那个文件。这时,使用-l(代表list)选项就可以不解压文件而列出某个zip文件的内容。

$ unzip -l moby.zip
Archive:  moby.zip
  Length     Date    Time    Name
 -------    ----     ----    ----
       0  01-26-06 18:40     bible/
  207254  01-26-06 18:40     bible/genesis.txt
  102519  01-26-06 18:19     bible/job.txt
1236574   01-26-06 18:19     moby-dick.txt
  508925  01-26-06 18:19     paradise_lost.txt
 --------                    -------
  2055272                    5 files

从这些结果可以看到,moby.zip中包含了两个文件(moby-dick.txtparadise_lost.txt)和一个目录(bible),该目录本身又包含两个文件:genesis.txtjob.txt。现在你就可以确切地知道在解压moby.zip后,会得到哪些东西了。使用-l命令有助于防止不小心错误地解压了一个包含100个文件的压缩文件,而实际上原本想解压的是一个包含100个文件的目录。前者解压后的文件将放得很凌乱,后者处理起来就容易多了。

-t

有时zip压缩文档会发生损坏。最坏的情况是,在解压zip压缩文档并删除它之后,才发现部分或者甚至全部解压后的内容都是损坏的,不能打开。最好是在实际解压文档之前,用-t(代表test)选项对文档进行测试。

$ unzip -t moby.zip
Archive: moby.zip 
    testing: bible/                   OK
    testing: bible/genesis.txt        OK
    testing: bible/job.txt            OK
    testing: moby-dick.txt            OK
    testing: paradise_lost.txt        OK
No errors detected in compressed data of moby.zip.

在每次处理zip压缩文档时,都应该使用-t选项。这是种明智的做法,虽然可能要多花些时间,但是值得这样做。

gzip

在某些方面,gzip的使用要比zip更简单些。使用zip,需要指定新创建的zip文件的名称,否则zip就不能运行。而使用gzip时,可以只输入命令和需要压缩的文件名。

$ ls -l
-rw-r--r-- scott scott 508925 paradise_lost.txt
$ gzip paradise_lost.txt
$ ls -l
-rw-r--r-- scott scott 224425 paradise_lost.txt.gz

不过,zipgzip之间还是有很大的区别。使用zip压缩文件时,它会保留原始文件,这样最终得到的是原始文件和新创建的压缩文件;而使用gzip压缩文件时,最终得到的只是新创建的压缩文件,原始文件不存在了。

如果想让gzip保留原始文件,则需要使用-c(或--stdout--to-stdout)选项,它可以将gzip的结果输出到shell,但还需要将输出重定向到另一个文件。如果使用-c选项时,忘记重定向输出,那么将得到如下这些无意义的内容:

很糟糕吧。相反,如果将输出重定向到一个文件呢?

$ ls -l
-rw-r--r-- 1 scott scott 508925 paradise_lost.txt
$ gzip -c paradise_lost.txt > paradise_lost.txt.gz
$ ls -l
-rw-r--r-- 1 scott scott 497K paradise_lost.txt
-rw-r--r-- 1 scott scott 220K paradise_lost.txt.gz

这样就好多了!现在既有原始文件,也有zip压缩的文件了。

提示 如果你不小心使用了-c选项,但又没有指定输出文件,按几次Ctrl+C组合键,就可以终止gzip的执行。

-r

如果你想对一个目录中的多个文件使用gzip,只需要使用通配符。不过,你不一定能够压缩想要的所有东西,如以下所示:

$ ls -F
bible/ moby-dick.txt paradise_lost.txt
$ ls -l *
-rw-r--r-- scott scott 1236574 moby-dick.txt
-rw-r--r-- scott scott 508925 paradise_lost.txt

bible:
-rw-r--r-- scott scott 207254 genesis.txt
-rw-r--r-- scott scott 102519 job.txt
$ gzip *
gzip: bible is a directory -- ignored
$ ls -l *
-rw-r--r-- scott scott 489609 moby-dick.txt.gz
-rw-r--r-- scott scott 224425 paradise_lost.txt.gz

bible:
-rw-r--r-- scott scott 207254 genesis.txt
-rw-r--r-- scott scott 102519 job.txt

注意,通配符并不能让gzip压缩bible目录中的所有东西,因为默认情况下,gzip不遍历压缩子目录的内容。为了让gzip可以压缩子目录,则需要在使用通配符的同时,使用-r(或--recursive)选项。

$ ls -F
bible/ moby-dick.txt paradise_lost.txt
$ ls -l *
-rw-r--r-- scott scott 1236574 moby-dick.txt
-rw-r--r-- scott scott 508925 paradise_lost.txt

bible:
-rw-r--r-- scott scott 207254 genesis.txt
-rw-r--r-- scott scott 102519 job.txt
$ gzip -r *
$ ls -l *
-rw-r--r-- scott scott 489609 moby-dick.txt.gz
-rw-r--r-- scott scott 224425 paradise_lost.txt.gz

bible:
-rw-r--r-- scott scott 62114 genesis.txt.gz
-rw-r--r-- scott scott 35984 job.txt.gz

这一次,每个文件(包括子目录中的文件)都会被压缩。不过要注意,每个文件都是单独压缩的。gzip命令并不能像zip命令那样将所有文件组合到一个大文件中。要想将所有文件压缩到一个大文件中,需要引入tar命令,这些将在8.17节中介绍。

-[0-9]

zip一样,用gzip压缩时也可以调整它使用的压缩率。gzip命令使用09来表示压缩率的度量标准:0表示“一点也不压缩”(和tar相似,稍后会看到),1表示“尽快完成压缩,而不需要压缩太多”,9表示“尽可能压缩文件,不介意会为此等待较长的时间”。默认值是6,但现在的计算机速度已经足够快,一直使用9表示的压缩率也不会有什么问题。

$ ls -l
-rw-r--r-- scott scott 1236574 moby-dick.txt
$ gzip -c -1 moby-dick.txt > moby-dick.txt.gz
$ ls -l
-rw-r--r-- scott scott 1236574 moby-dick.txt
-rw-r--r-- scott scott 571005 moby-dick.txt.gz
$ gzip -c -9 moby-dick.txt > moby-dick.txt.gz
$ ls -l
-rw-r--r-- scott scott 1236574 moby-dick.txt
-rw-r--r-- scott scott 487585 moby-dick.txt.gz

记得要使用-c选项,并将结果通过管道输出到实际的.gz文件,如8.7节中介绍的那样。

说明 如果想要更灵活些,可以在.bashrc文件中为gzip命令定义一个别名,如下所示:

  alias gzip= ' gzip -9 '

这样一来,就能总是使用-9的压缩率,而不用再刻意考虑它了。

gunzip

gunzip命令可以轻松地解压gzip压缩的文档。

$ ls -l
-rw-r--r-- scott scott 224425 paradise_lost.txt.gz
$ gunzip paradise_lost.txt.gz
$ ls -l
-rw-r--r-- scott scott 508925 paradise_lost.txt

gzip会删除原始文件,只保留gzip压缩结果一样,gunzip也会删除.gz文件,只保留最终gunzip解压的结果。如果确实需要保留二者,则需要使用-c选项(或--stdout--to-stdout),将结果通过管道输出到想要创建的文件。

$ ls -l
-rw-r--r-- scott scott 224425 paradise_lost.txt.gz
$ gunzip -c paradise_lost.txt.gz > paradise_lost.txt
$ ls -l
-rw-r--r-- scott scott 508925 paradise_lost.txt
-rw-r--r-- scott scott 224425 paradise_lost.txt.gz

使用-c选项可能是个好主意,特别是你计划保留.gz文件,或者想把它发送给其他人。当然你也可以再使用gzip命令来创建自己的压缩文档,但是为何要多此一举呢?

说明 如果你不喜欢使用gunzip命令,也可以使用gzip –d(或--decompress--uncompress)。

-t

在用gunzip命令解压某个(或某些)文件之前,或许你想验证一下文件能否正确解压,所有文件都不会有任何损坏。为此,可以使用-t(或--test)选项。

$ gzip -t paradise_lost.txt.gz
$

这样就表示一切正常,如果压缩文档没有问题,gzip不会报告任何信息。如果压缩文档有问题,你会得到提示信息。这多少会让人觉得有点不放心,但是基于Unix的系统就是这样的。通常,只有在发生了需要你知道的问题时,才会发出提示。如果一切都运行正常,则不会提示任何信息。

bzip2

如果你使用gzip很顺手的话,那么使用bzip2也会相当容易,因为bzip2的创建者有意地将这个新命令的选项和行为设计得和它的前身尽可能相似。

$ ls -l
-rw-r--r-- scott scott 1236574 moby-dick.txt
$ bzip2 moby-dick.txt
$ ls -l
-rw-r--r-- scott scott 367248 moby-dick.txt.bz2

gzip一样,bzip2也只保留最终生成的.bz2文件,原始的moby-dick.txt文件将不复存在。若要保留原始的文件,可以使用-c(或--stdout)选项,并将结果通过管道输出到文件名以.bz2结尾的文件中。

$ ls -l
-rw-r--r-- scott scott 1236574 moby-dick.txt
$ bzip2 -c moby-dick.txt > moby-dick.txt.bz2
$ ls -l
-rw-r--r-- scott scott 1236574 moby-dick.txt
-rw-r--r-- scott scott 367248 moby-dick.txt.bz2

回头看看8.8节,你会发现gzipbzip2的用法相当类似,这正是bzip2设计的初衷。

-[0-9]

zipgzip一样,用bzip2压缩时也可以调整它使用的压缩率。bzip2命令使用09来表示压缩率的度量标准:0表示“一点也不压缩”(和tar相似,稍后会看到),1表示“尽快完成压缩,而不需要压缩太多”,9表示“尽可能压缩文件,不介意会为此等待较长的时间”。默认值是6,但现在的计算机速度已经足够快,一直使用9表示的压缩率也不会有什么问题。

$ ls -l
-rw-r--r-- scott scott 1236574 moby-dick.txt
$ bzip2 -c -1 moby-dick.txt > moby-dick.txt.bz2
$ ls -l
-rw-r--r-- scott scott 1236574 moby-dick.txt
-rw-r--r-- scott scott 424084 moby-dick.txt.bz2
$ bzip2 -c -9 moby-dick.txt > moby-dick.txt.bz2
$ ls -l
-rw-r--r-- scott scott 1236574 moby-dick.txt
-rw-r--r-- scott scott 367248 moby-dick.txt.bz2

压缩率为1时的结果是424 KB,而压缩率为9时的结果是367 KB,区别相当大!还要注意用gzipbzip2命令最终生成的文件在大小上的差异。都是用-9代表的压缩率,gzip压缩后的moby-dick.txt大小为488 KB,而bzip2将文件大小进一步压缩至367 KB。bzip2命令的压缩速度明显要比gzip命令慢,但是在速度快的计算机上,bzip2只不过比gzip多花两三秒钟的时间,通常无需担心这个问题。

说明 如果想要更灵活些,可以在.bashrc文件中为gzip命令定义一个别名,如下所示:

  alias bzip2='bzip2 -9'

这样一来,就能总是是使用-9的压缩率,而不用再可以考虑它了。

bunzip2

bzip2在设计上有意要尽可能地模仿gzip一样,bunzip2gunzip的工作原理也非常相似。

$ ls -l
-rw-r--r-- scott scott 367248 moby-dick.txt.bz2
$ bunzip2 moby-dick.txt.bz2
$ ls -l
-rw-r--r-- scott scott 1236574 moby-dick.txt

可以看到,bunzip2gunzip在许多方面有相似之处:二者都会删除原始的压缩文件,只保留最终的解压结果。如果确实需要既保留原来的压缩文件,也保留最终的解压文件,则需要使用-c选项(或--stdout--to-stdout),并将结果通过管道输出至想要创建的文件。

$ ls -l
-rw-r--r-- scott scott  367248 moby-dick.txt.bz2
$ bunzip2 -c moby-dick.txt.bz2 > moby-dick.txt
$ ls -l
-rw-r--r-- scott scott 1236574 moby-dick.txt
-rw-r--r-- scott scott  367248 moby-dick.txt.bz2

命令之间能够互相模仿它们的选项和行为是件很好的事情,这样使得它们易于学习。在这方面,bzip2bunzip2的创建者表现出了非凡的远见。

说明 如果你不喜欢使用bunzip2,也可以使用bzip2 –d(或—decomp- ress--uncompress)。

-t

在用bunzip命令解压某个(或某些)文件之前,或许你想验证一下文件能否正确解压,所有文件都不会有任何损坏。可以使用-t(或--test)选项完成这个操作。

$ bunzip2 -t paradise_lost.txt.gz
$

gunzip一样,如果压缩文档没有问题,bunzip2也不会报告任何信息。如果压缩文档有问题,你会得到提示信息。

-cf

记住,tar不能进行压缩,只进行归档(顺便提一下,结果生成的归档文件就是tarball)。实际上tar是使用其他程序(比如gzipbzip2)来压缩它要创建的归档文件。即使你不打算压缩tarball,也仍然可以用同样的基本选项以同样的方式来创建它:-c(或--create)选项告诉tar正在创建一个tarball,而-f(或--file)选项则用于为tarball指定文件名。

$ ls -l
scott scott  102519 job.txt
scott scott 1236574 moby-dick.txt
scott scott  508925 paradise_lost.txt
$ tar -cf moby.tar *.txt
$ ls -l
scott scott  102519 job.txt
scott scott 1236574 moby-dick.txt
scott scott 1853440 moby.tar
scott scott  508925 paradise_lost.txt

这里需要注意两点。首先,把job.txtmoby-dick.txtparadise_ lost.txt的大小加起来,结果是1848018 B。与moby.tar的大小相比,你会发现tar ball大了5422 B。记住,tar只是归档工具,而不是压缩工具,所以结果至少与把各个文件加在一起的总大小相同,还要加上一些用于保存tar ball内部文件内容的开销。其次,与gzipbzip2不一样的是,tar会保留原始的文件。考虑到tar命令的背景只是一个备份工具,这一点也就不足为奇了。

tar命令真正令人称道的地方是,它可以用于压缩整个目录结构,所以能用它来批量归档大量的文件和子目录。

$ ls -lF
drwxr-xr-x scott scott 168 moby-dick/
$ ls -l moby-dick/*
scott scott  102519 moby-dick/job.txt
scott scott 1236574 moby-dick/moby-dick.txt
scott scott  508925 moby-dick/paradise_lost.txt

moby-dick/bible:
scott scott 207254 genesis.txt
scott scott 102519 job.txt
$ tar -cf moby.tar moby-dick/
$ ls -lF
scott scott     168 moby-dick/
scott scott 2170880 moby.tar

tar命令永远都会有用,原因显而易见:它真的太棒了!把tar和压缩工具结合起来以后,它的功能会变得更加强大,下一节就介绍这种用法。

-zcvf

看看8.7节和8.12节,思考一下其中讨论的内容,或许你会想到一个问题。如果想要压缩一个包含100个文件的目录,而且这些文件都位于不同的子目录中,会怎么样?如果使用gzipbzip2-r(代表recursive)选项,最终得到的是100个单独的压缩文件,每个文件都整齐地保存在原先的子目录中。毫无疑问,这肯定不是你想要的结果。怎么可能会在一封电子邮件里面附加100个.gz.bz2文件的附件呢?不可能的嘛!

这就是tar显示身手的地方了。首先使用tar命令对目录及其内容(位于各个子目录中的100个文件)进行归档,然后再使用gzipbzip2对生成的tarball文件进行压缩。因为gzip是与tar配合使用时最常用的压缩程序,所以在此重点介绍它们的组合使用。如下所示:

$ ls -l moby-dick/*
scott scott  102519 moby-dick/job.txt
scott scott 1236574 moby-dick/moby-dick.txt
scott scott  508925 moby-dick/paradise_lost.txt

moby-dick/bible:
scott scott 207254 genesis.txt
scott scott 102519 job.txt
$ tar -cf moby.tar moby-dick/ | gzip -c >
moby.tar.gz
$ ls -l
scott scott  168 moby-dick/
scott scott   20 moby.tar.gz

这种方法可行,但需要输入的信息太多!还有一种简单得多的方法,应该作为默认方法。这种方法涉及tar命令的两个新选项:-z(或--gzip)选项,能够在tar中调用gzip,这样就不需要手工调用了;-v(或--verbose)选项,虽然在此处并不真正需要,但总是有一定用处,因为它可以在tar运行时通知其运行状况。

$ ls -l moby-dick/*
scott scott  102519 moby-dick/job.txt
scott scott 1236574 moby-dick/moby-dick.txt
scott scott  508925 moby-dick/paradise_lost.txt

moby-dick/bible:
scott scott 207254 genesis.txt
scott scott 102519 job.txt
$ tar -zcvf moby.tar.gz moby-dick/
moby-dick/
moby-dick/job.txt
moby-dick/bible/
moby-dick/bible/genesis.txt
moby-dick/bible/job.txt
moby-dick/moby-dick.txt
moby-dick/paradise_lost.txt
$ ls -l
scott scott    168 moby-dick
scott scott 846049 moby.tar.gz

通常,用targzip归档和压缩文件,最终的文件扩展名是.tar.gz。但是如果你愿意,也可以使用.tgz.tar.gzip

说明 完全有可能用bzip2来代替gzip,让tarbzip2配合使用,命令的用法如下所示(注意-j选项,它正是引入bzip2的关键):

  $ tar -jcvf moby.tar.bz2 moby-dick/

在这个例子中,最终生成的文件扩展名应该是.tar.bz2。不过,也可以使用.tar.bzip2.tbz2.tbz。如果用gzipbzip2都可以生成文件名以.tbz结尾的文件,这必定会造成很大的混淆。这就是要使用特定的文件扩展名的一个强有力的证据,只有这样才可以将这种混淆降低到最低的限度。

-zvtf

在解开tarball(不管它是否用gzip压缩过)之前,最好先对它进行测试。首先,这能提前了解tarball是否有损坏,免得文件出错时再急得抓耳挠腮。其次,能够知道创建tarball的人是细心地将100个文件放在一个目录中,还是粗心地将100个单独的文件直接放在tarball中,以免解开tarball后弄得桌面上到处都是文件。

要测试tarball(再次假设还用gzip压缩的),可以使用-t(或--list)选项。

$ tar -zvtf moby.tar.gz
scott/scott 0 moby-dick/
scott/scott 102519 moby-dick/job.txt
scott/scott 0 moby-dick/bible/
scott/scott 207254 moby-dick/bible/genesis.txt
scott/scott 102519 moby-dick/bible/job.txt
scott/scott 1236574 moby-dick/moby-dick.txt
scott/scott 508925 moby-dick/paradise_lost.txt

这样就可以知道每个文件的权限、所有者、大小以及创建时间等信息。此外,因为每个文件的路径都以moby-dick/开头,所以最终得到的是一个目录,tarball中的所有文件和子目录就包含在这个目录中。

要确保-f是最后一个选项,因为在它后面需要指定.tar.gz文件的名称;否则,tar会报错:

$ tar -zvft moby.tar.gz
tar: You must specify one of the `-Acdtrux'  options
Try `tar --help' or `tar --usage'  for more
information.

在确信.tar.gz文件没有损坏之后,就该真正解开它了,8.19节将介绍相关的操作。

说明 如果正在测试的tarball是用bzip2压缩的,则应该使用以下命令:

  $ tar -jvtf moby.tar.bz2

-zxvf

要创建.tar.gz文件,使用的一组选项是-zcvf。而要解开并解压生成的tarball,只需要替换其中的一个选项,即用-x(或--extract)替换-c(或--create)。

$ ls -l
rsgranne rsgranne 846049 moby.tar.gz
$ tar -zxvf moby.tar.gz
moby-dick/
moby-dick/job.txt
moby-dick/bible/
moby-dick/bible/genesis.txt
moby-dick/bible/job.txt
moby-dick/moby-dick.txt
moby-dick/paradise_lost.txt
$ ls -l
rsgranne rsgranne    168 moby-dick
rsgranne rsgranne 846049 moby.tar.gz

在打开文件之前,要确保对它进行测试,如8.18节所述。这意味着运行的命令的顺序将如下所示:

$ tar -zvtf moby.tar.gz
$ tar -zxvf moby.tar.gz

说明 如果正在打开的tarball是用bzip2压缩的,则应该使用以下命令:

  $ tar -jxvf moby.tar.bz2

在原来使用低速调制解调器和小容量硬盘的时代,归档和压缩是非常必要的。如今,归档和压缩更多是为了方便,但也还是必不可少的。例如,如果你曾经下载了需要编译的源代码,很可能就会发现下载的是类似sourcecode.tar.gz这样的文件。以后还可能会看到更多以.tar.bz2为后缀的文件名。如果与Windows用户交换文件,那么遇到的可能就是以.zip为后缀的文件名。正因为归档和压缩工具的使用将远超出你的想象,所以应该好好学习使用这些工具。



第三部分 查找资料

第9章 查找资料:就这么简单

第10章 find命令



第9章 查找资料:就这么简单

硬盘空间的容量一年比一年大,而价格一年比一年低,是件很棒的事。现在我们拥有很多技术玩具(如数码相机、摄像机、MP3播放器及在网上下载到的各种电影和音乐),肯定需要保存在硬盘上。不过,每位电子资料收藏家都要为此付出代价,他们经常找不到自己想要的东西。比如,很难在10 000张图片中找到自己兄弟的某张照片,或者翻遍了600个文档也没有找到自己写的那篇文章。幸运的是,Linux为用户提供了强大的查找工具,能够快速而有效地找到需要的文件。

locate

知道文件的名称,或者名称的一部分,但不记得放在系统的哪个位置。这就是locate命令要解决的问题。locate命令能够在文件、程序和目录中查找与提供的搜索项匹配的内容,并把任何匹配的结果依次显示到终端界面上。

说明 为了节省篇幅,此处用省略号代替路径的前面一部分-/home/ scott -

$ locate haggard
.../txt/rider_haggard
.../txt/rider_haggard/Queen_of_the_Dawn.txt
.../txt/rider_haggard/Allan_and_the_Ice-Gods.txt
.../txt/rider_haggard/Heu-Heu_or_The_Monster.txt

搜索结果出来得非常快,因为locate命令并没有对系统进行实时搜索。实际上,它搜索的是每天自动更新的文件名数据库(请参照9.4节了解更多内容)。因为locate搜索的是早已创建好的数据库,所以搜索结果几乎能够瞬间显示出来。

不过,在你的计算机上使用的可能不是locate命令,而是slocate命令,只是你没有意识到而已。slocate(代表secure locate)命令的版本更新些,它不会搜索运行slocate命令的用户没有权限查看的目录(例如,如果你不是root用户,那么当用locate搜索时,就不会显示/root目录中的结果)。在slocate问世之前,locate经常反馈很多错误,抱怨权限问题;而有了slocate以后,这些错误就不复存在了。

要体验一下slocate是如何运行的,可以尝试以下命令。注意,第一次搜索是以普通用户的身份执行的,而不是root用户,搜索将失败。使用su命令成为root用户,再次运行locate命令,这次就成功了。搜索结果如下所示(顺便说一下,slocate.dbslocate命令使用的数据库文件)。

$ locate slocate.db
$ su -
# locate slocate.db
/var/lib/slocate/slocate.db.tmp
/var/lib/slocate/slocate.db

不过,为了简化用户的操作,大多数系统都为/usr/bin/locate创建了一个软链接,让它指向/usr/bin/slocate。要验证一下你的Linux分发版是否也是这样的,可以试试以下命令(这里显示的特定结果来自一台运行K/Ubuntu 5.10的计算机,这是一种基于Debian的Linux分发版,删除了一些数据,以便突出重要的信息):

$ ls -l /usr/bin/locate
root root /usr/bin/locate -> slocate

因为运行slocate对用户来说是透明的(也就是说,用户看不到他运行的是slocate),而且locate使用的字母更少一些,输入得更快,所以本书也继续使用locate,虽然实际运行的命令是slocate

locate -i

在上一节中,测试locate命令时,使用haggard这个单词搜索任何名称中包含它的文件或目录,这样就找到了一组放在公共目录中的H. Rider Haggard的小说。结果如下所示:

$ locate haggard
.../txt/rider_haggard
.../txt/rider_haggard/Queen_of_the_Dawn.txt
.../txt/rider_haggard/Allan_and_the_Ice-Gods.txt
.../txt/rider_haggard/Heu-Heu_or_The_Monster.txt

之所以能找到这些小说,是因为包含它们的目录名称中有haggard这个单词。但如果目录名称是H_Rider_Haggard,搜索就会失败,因为Linux系统默认是区分大小写的(如第1章所述)。明确这一点以后,可以加上-i选项,这样执行的就是不区分大小写的搜索,从而既可以找到路径中包含haggard的文件,也可以找到包含Haggard的文件(实际上,也包括HAGGARD、HaGgArD等)。

$ locate -i haggard
/txt/rider_haggard
/txt/rider_haggard/Queen_of_the_Dawn.txt
/txt/rider_haggard/Allan_and_the_Ice-Gods.txt
/txt/rider_haggard/Heu-Heu_or_The_Monster.txt
/txt/Rider_Haggard
/txt/Rider_Haggard/King_Solomons_Mines.txt
/txt/Rider_Haggard/Allan_Quatermain.txt

结果表明,这种方法找到的Haggard小说比第一次要更多。当需要让locate命令返回更多的结果时,要记得使用-i选项;否则,可能会遗漏很多你想要找到的重要文件和文件夹。

说明 有关H. Rider Haggard的更多信息,可以访问http://en.wikipedia.org/wiki/Rider_Haggard。他是个有趣的人,虽然他已经过世了,也可以读读这些信息。

-n

如果你经常用locate命令,最终会遇到类似以下的情况:

$ locate pdf
/etc/cups/pdftops.conf
/etc/xpdf
/etc/xpdf/xpdfrc-latin2

一下子找到了2 373个结果。这也太多了吧! 其实最好应该按以下方式使用这条命令:

$ locate pdf| less

locate搜索结果通过管道输出至分页器less命令(在5.5节中介绍过这个命令),让这2 373个结果一次只在屏幕上显示一屏。

如果只想看前面的x个结果(x是你选择的一个整数),则可以使用-n选项,后面跟着你想要看的结果个数。

$ locate -n 3 pdf
/etc/cups/pdftops.conf
/etc/xpdf
/etc/xpdf/xpdfrc-latin2

这样就更容易管理了,可能这样的方式才是你需要的效果。不应该让locate命令像发洪水一样,一次就把所有结果显示出来。实际上,应该控制locate命令输出结果的数量,根据自己的需要使用这个命令。

updatedb

9.1节在介绍locate命令时曾经提到:该命令的执行之所以这么快,是因为它实际上搜索的是一个包含计算机中所有文件和目录名称的数据库。在安装locate时,就将它自动设置为扫描硬盘和更新数据库(通常是在午夜时进行)。这样设置确实很方便,但如果需要查找刚在计算机上放置的文件,就不是很方便了。

例如,如果你安装了Rootkit Hunter(一个用于查找rootkits的程序,黑客经常用rootkits来控制你的Linux计算机),接着想看看程序安装的文件。在这种情况下,locate命令就帮不上忙了,因为它现在还不知道这些新安装好的文件,而且直到它的数据库得以更新之前,locate命令都不会知道这些新文件的存在。不过,在任何时候都能够通过运行updatedb,手动更新locate使用的数据库。因为这个命令基本上会为计算机上的每个文件和文件夹编制索引,所以需要以root用户的身份来运行它(或使用K/Ubuntu这样Linux系统中的sudo命令,这些系统不鼓励使用root)。

# apt-get install rkhunter
# exit
$ locate rkhunter
$ su -
# updatedb
# exit
$ locate rkhunter
/usr/local/rkhunter
/usr/local/rkhunter/bin
/usr/local/rkhunter/etc

在上述命令中,首先安装了rkhunter(Rootkit Hunter的程序包名),然后退出root。之后搜索rkhunter,但没有结果。再成为root用户,运行updatedb,扫描硬盘空间,让locate数据库知道新发生的任何变化,然后退出root。最后再使用locate搜索rkhunter,这次就搜索到了。

但这里应该注意一件事:updatedb运行的速度与硬盘中资料的数量以及计算机的处理速度有直接关系。处理器的速度越快、硬盘的速度越快、文件越少,updatedb也就运行得越快。如果CPU很慢,硬盘的转速才有5 400 RPM,而且还有100万个文件,那就别指望updatedb能快到哪去。如果想知道updatedb的运行花费了多长时间,可以在updatedb前面加上time命令,如下所示:

# time updatedb

updatedb处理完成以后,time命令就能显示整理好locate数据库花费了多长时间。这个信息很有用,需要记住,可能以后会需要使用updatedb而时间又很紧。

说明 实际上,updatedb命令与slocate -u完全相同,而且updatedb实际上只是一个指向slocate的链接,很容易就可以搞清楚这一点。

  $ ls -l /usr/bin/updatedb
  root root /usr/bin/updatedb -> slocate

grep

locate命令可以搜索文件和目录的名称,但是不能搜索文件的内容。要搜索文件内容,应该使用grep命令。grep的处理过程基本上就是,为它提供一个搜索想要匹配的模式,指定要搜索的一个或一组文件(甚至是整个硬盘空间),然后grep就会输出能够与搜索模式匹配的各行内容的列表。

$ grep pain three_no_more_forever.txt
all alone and in pain

这个例子是用grep来检查一个文件中是否包含pain这个词(文件的内容是Peter Von Zer Muehlen写的一首名为“Three No More Forever”的诗)。当然,这个文件中有pain这个词,所以grep就在终端界面上打印出包含搜索内容的那一行。但如果想一次在多首Peter所写的诗中进行搜索,又该怎么办呢?使用通配符可以解决这一问题!

$ grep pain *
fiery inferno in space.txt:watch the paint peel,
three_no_more_forever.txt:all alone and in pain
the speed of morning.txt:of a Chinese painting.
8 hour a day.txt:nice paint job too
ghost pain.txt:Subject: ghost pain

注意,grep找出了所有使用pain字符串的地方,包括paintpainting。还要注意grep是如何显示每个包含搜索内容的文件名,以及包含搜索内容的相应文本行的。到目前为止,用grep在文件中进行搜索还是相当简单的。所以现在应该把搜索复杂化一些,这正是下一节将要介绍的内容。

上一节介绍了如何在一组文件中搜索匹配的特定模式,你应该对grep的原理有所了解了。那是对grep最基础的使用,但现在需要让搜索再复杂些,为此需要先深入理解一下grep搜索使用的匹配模式。构建这些模式使用了Linux工具箱中最强大的工具:正则表达式(regular expression或regex)。如果要充分利用grep,就需要透彻地理解正则表达式。讲述正则表达式的内容就得用一整本书,这里介绍的只是正则表达式的基本知识。

提示 想更多地学习正则表达式,可以在因特网上找到大量的学习资源,而且Sams Teach Yourself Regular Expressions in 10 Minutes(由Ben Forta撰写,ISBN:0672325667)是一本相当好的书,能够真正帮助你探索和学习正则表达式。

在刚开始使用grep时,新用户会对这个命令有好几种版本感到很困惑,如表9-1所示。

表9-1 grep的不同版本
支持的模式 grep命令选项 单独的命令
基本的正则表达式 grep -G(或 --basic-regexp grep
扩展的正则表达式 grep -E(或 --extended-regexp egrep
固定字符串的列表,匹配其中任何一项 grep -F(或 --fixed-strings fgrep
Perl正则表达式 grep -P(或 --perl-regexp 不可用

从表9-1中可以看到,grep支持基本的正则表达式。如果使用-E(或--extended-regexp)选项,或直接使用egrep命令,就能够使用扩展的正则表达式。大多数情况下,这或许就是你要做的,除非要执行的搜索非常简单。其他两个选择更复杂些:grep-F(或--fixed-strings)选项,或者直接用fgrep命令,支持同时使用多个要匹配的搜索项;grep-P(或--perl-regexp)选项,可以让Perl编程高手使用某些Perl语言特有的正则表达式用法。

说明 在本书中,除非特别声明,使用的都是普通的grep命令和基本的正则表达式。

在继续学习之前,需要澄清几点可能造成混淆的地方。如果你对这些内容还有任何不清楚的地方,请使用列出的相关资源作为进一步学习的起点。

通配符不等于正则表达式。虽然通配符和正则表达式都使用星号(*)字符,但它们的含义完全不同。通配符中使用特定的字符(如?*)表示替换(substitution),而正则表达式中同样的字符表示要对前面的内容进行匹配的次数。例如,在通配符中,可以把c?t中的?替换成另一个字符,而且只能替换一次,所以这个通配符可以匹配catcotcut,但不能与ct匹配。在正则表达式中,c[a-z]?t中的?表示从字母A到Z都可以匹配,但只可以匹配0次或1次,所以这个正则表达式可以匹配catcotcut,也可以匹配ct

提示 有关通配符和正则表达式之间区别的更多信息,可以看看“What Is a Regular Expression”(http://docs.kde.org/stable/en/kdeutils/KRegExpEditor/whatIsARegExp.html),“Regular Expressions Explained”(www.castaglia.org/proftpd/doc/contrib/regexp.html),以及“Wildcards Gone Wild”(www.linux-mag.com/2003-12/power_01.html)。

另一个有关grep潜在的容易混淆的问题是,需要识别grep正则表达式中使用的特殊字符。例如,在正则表达式中,字符串[a-e]表示要匹配一个范围,这意味着它可以匹配abcde之间的任意字符。当在grep中使用[]这两个字符时,你需要让shell明确地知道[]字符是作为正则表达式中范围定义的分隔符,还是要搜索的词语中的一部分。需要注意的特殊符号有以下几个:

. ? [ ] ^ $ | \

最后,正则表达式中的单引号和双引号的用法也有很大区别。单引号('')是在告诉shell正在搜索一个字符串,而双引号("")则是让shell知道想要使用shell变量。例如,用grep和正则表达式,按照以下方式在一个朋友的诗歌中搜索所有用到的“hey you!”这个短语的地方,就没有成功。

$ grep hey you! *
grep: you!: No such file or directory
txt/pvzm/8 hours a day.txt:hey you! let's run!
txt/pvzm/friends & family.txt:in patience they wait
txt/pvzm/speed of morning.txt:they say the force

原因是只写了“hey you!”,没有用任何引号把搜索内容括起来,grep就不能明白你的意图了。它首先试图在名为“you!”的文件中搜索“hey”这个词,但没有成功,因为根本就不存在这个文件。然后它在当前工作目录中的每个文件(按照通配符*的指示)中搜索“hey”,结果找到三个匹配的内容。三个搜索结果中的第一个确实包含你要找的那个短语,所以从这一点来看,搜索好像是有效的,但事实并非如此。这种搜索很原始,并不总能表达你想要的结果。让我们再试一次。

这次用双引号将搜索内容括起来,就能修复原来不用任何引号而带来的问题了。

$ grep "hey you!" *
bash: !" *: event not found

这次更糟糕了! 实际上,双引号也会导致出现大问题,甚至产生比刚才看到的问题还要糟糕。怎么回事呢?叹号(!)是一个shell命令,用于引用命令历史。通常是在叹号(!)后面跟上一个PID(process ID,进程的ID)号,代表先前你运行过的命令,如!264

所以在这里,bash看到叹号(!)后,就查找跟在它后面的PID,然后报错,说它找不到先前运行过的名为" *(一个双引号、一个空格和一个星号)的命令,这真是个不可思议的命令。

事实表明,引号表示你正在搜索内容中使用shell变量,其实这根本不是你想要的效果。所以,在这儿也不能直接用双引号。试试单引号,如下所示:

$ grep 'hey!' *
txt/pvzm/8 hours a day.txt:hey you! let's run!

结果好多了。单引号告诉grep搜索内容不包含任何shell变量,只是一串需要匹配的字符。你瞧,现在只有一个结果,正是你要找的那个。

从这些例子中获得了哪些收获呢?什么时候应该使用单引号,什么时候应该使用双引号,什么时候任何引号都不使用,现在应该清楚了。如果要搜索精确的匹配结果,就使用单引号;如果要把shell变量结合到搜索内容中(很少有这样的需要),就使用双引号;但如果搜索关键字只包含数字和字母,完全不使用任何引号也没有问题。如果想要安全些,放心地使用单引号吧,即使只有一个词,也可以加上单引号,这没什么损害。

-R

星号(*)通配符可以一次搜索同一目录中的多个文件,但要搜索多个子目录,就需要使用-R(或--recursive)选项。让我们在一组文件中搜索一下hideous这个词,这些文件都是我喜欢的19世纪和20世纪初期的恐怖小说作家的作品,虽然这些小说已经过时了,但仍然很精彩。

$ grep -R hideous *
machen/great_god_pan.txt:know, not in
your most fantastic, hideous dreams can you have
machen/great_god_pan.txt:hideously
contorted in the entire course of my practice, and I
machen/great_god_pan.txt:death was
horrible. The blackened face, the hideous form upon
lovecraft/Beyond the Wall of Sleep.txt:some hideous
but unnamed wrong, which
lovecraft/Beyond the Wall of Sleep.txt:blanket over
the hideous face, and awakened the nurse.
lovecraft/Call of Cthulhu.txt:hideous a chain. I
think that the professor, too, intended to
lovecraft/Call of Cthulhu.txt:voodoo meeting;
and so singular and hideous were the rites
lovecraft/Call of Cthulhu.txt:stated, a very
crude bas-relief of stone, comprising a hideous...

提示 当然,如果显示的结果太多,则应该通过管道把结果输出到less命令,如9.3节中对locate命令的结果进行的处理一样。

  $ grep -R hideous *| less

另一种办法就是把命令的输出发送到一个文本文件中,然后使用任何你喜欢的文本编辑器打开:

  $ grep -R hideous * > hideous_in_horror.txt

如果需要把搜索结果保存起来以备后用,这个方法不错。

-i

在默认情况下,grep执行的搜索是区分大小写的。在前一节中,在H.P. Lovecraft的小说中搜索的是hideous(那个作者最喜欢用的一个词),而搜索Hideous会怎么样呢?

$ grep Hideous h_p_lovecraft/*
h_p_lovecraft/the_whisperer_in_darkness.txt: them.
Hideous though the idea was, I knew...

前面搜索hideous的结果有463条(真多!),而现在搜索Hideous的结果只返回了1条。有什么办法能够把这两种结果组合起来呢?使用-i(或--ignore-case)选项就能同时搜索二者,而且还能搜索HiDeOuS、HIDEOUS以及其他可能的组合。

$ grep -i hideous h_p_lovecraft/*
h_p_lovecraft/Call of Cthulhu.txt:voodoo meeting;
and so singular and hideous were the rites
h_p_lovecraft/Call of Cthulhu.txt:stated, a very
crude bas-relief of stone, comprising a hideous
h_p_lovecraft/the_whisperer_in_darkness.txt: them.
Hideous though the idea was, I knew...

记住,这个选项可能导致结果数量急速增加,或许会达到另一个数量级别。如果这是一个问题的话,可以参考上一节结尾的提示,看看针对这种问题而给出的建议。

-w

回想一下在9.6节中,第一次学习使用grep命令。你要搜索pain这个词,而grep命令忠实地返回一列结果,告诉你什么位置使用了pain。

$ grep pain *
fiery inferno in space.txt:watch the paint peel,
three_no_more_forever.txt:all alone and in pain
the speed of morning.txt:of a Chinese painting.
8 hour a day.txt:nice paint job too
ghost pain.txt:Subject: ghost pain

在默认情况下,grep命令搜索字符串pain所有可能出现的位置,显示包含pain的整行内容,包括paint和painting。如果搜索的文件中包含painless、Spain、或painstaking,包含这些词的行也会显示出来。但如果只需要精确匹配pain的行,又该怎么办呢?这时就要使用-w(或--word-regexp)选项。

$ grep -w pain *
three_no_more_forever.txt:all alone and in pain
ghost pain.txt:Subject: ghost pain

当搜索结果太多时,这个选项确实能够缩小结果范围,可以轻松找出需要的内容。

-n

grep命令能够显示包含正在搜索内容的那一行,但并不能指出在文件中的哪个位置能够找到那行信息。要找到行号,可以利用-n(或--line-number)选项。

$ grep -n pain *
fiery inferno in space.txt:56:watch the paint peel,
three_no_more_forever.txt:19:all alone and in pain
the speed of morning.txt:66:of a Chinese painting.
8 hour a day.txt:78:nice paint job too
ghost pain.txt:32:Subject: ghost pain

现在知道每个pain字符串实例所在行的行号了,几乎在任何文本编辑器中都可以直接跳转到指定行。真是太棒了!

$ ls -1 | grep 1960

grep命令单独使用时就很强大,但把它作为其他程序输出的过滤器来使用,才真正显示出它的光彩。例如,假设你把所有John Coltrane的MP3按专辑(是的,总共66个,Coltrane相当优秀)组织到单独的子文件夹中,每个文件夹的名称以年份作为开始。部分列表如下所示(ls命令使用了-1选项,所以每行只显示一个结果):

$ ls -1
1956_Coltrane_For_Lovers
1957_Blue_Train
1957_Coltrane_[Prestige]
1957_Lush_Life
1957_Thelonious_Monk_With_John_Coltrane

现在,如果你只想看看自己所有的Coltrane在1960年发行的专辑,该怎么办呢?将ls -1的结果通过管道输出到grep,几秒钟内就能得到答案。如下所示:

$ ls -1 | grep 1960
1960_Coltrane_Plays_The_Blues
1960_Coltrane's_Sound
1960_Giant_Steps
1960_My_Favorite_Things

在经过思考以后,你会发现按这种方式来使用grep,其用法至少也有几百种。这里再演示另一种功能强大的用法。ps命令可以列出运行中的进程,而-f选项能够让ps提供完整的列表,其中包括每个进程的很多信息;而-U选项,后面跟上用户名称,可以限制只输出那个用户拥有的进程。通常,ps -fU scott会产生一个很长的列表,如果要搜索特定进程的信息,这种输出就太长了。不过,在grep的帮助下,就能够轻松地限制输出内容。

说明 为了节省篇幅,这里删除了一些使用ps命令通常可以看到的信息。

$ ps -fU scott | grep firefox
scott 17623 /bin/sh /opt/firefox/firefox
scott 17634 /opt/firefox/firefox-bin
scott 1601 grep firefox

ps命令会列出用户scott拥有的全部进程(实际上有64个),但将输出通过管道发给grep命令之后,只列出其中包含firefox的那些进程。遗憾的是,输出的最后一行是错误的。你关心的只是实际的Firefox程序,而不是用grep搜索firefox这样的信息。要在grep结果中隐藏firefox的搜索信息,试试以下命令:

$ ps -fU scott | grep [f]irefox
scott 17623 /bin/sh /opt/firefox/firefox
scott 17634 /opt/firefox/firefox-bin

现在,grep的搜索内容是通过正则表达式的一个范围(从f到f)来定义的,用它可以找到ps输出中包含firefox的那些行(表示相应的进程正在运行Firefox)。不过,它不匹配grep行,因为这行的内容实际上是[f]irefox,所以不匹配。这里grep命令也不能匹配原始的命令字符串ps -ef | grep [f]irefox,因为它包含了[]字符,而用于搜索[f]irefoxgrep在运行时搜索的是精确的“firefox”这个词。这点难理解了吧,不过,亲自试一试,多仔细想想,就能领悟其意义了。

-A, -B, -C

在处理数据时,上下文至关重要。前面学过,grep可以输出包含搜索内容的实际行,但也可以让grep输出匹配内容之前和之后的若干行。在9.11节中,曾经用grep来搜索John Coltrane专辑。他最好的专辑是A Love Supreme,那么这个专辑之前的三个专辑又是什么呢?使用-B(或--before-context=#)选项,就可以得到答案。如下所示:

$ ls -1 | grep -B 3 A_Love_Supreme
1963_Impressions
1963_John_Coltrane_&_Johnny_Hartman
1963_Live_At_Birdland
1964_A_Love_Supreme

如果要找出A Love Supreme之后的唱片,则应该使用-A(或--after-context=#)选项。如下所示:

$ ls -1 | grep -A 3 'A_Love_Supreme'
1964_A_Love_Supreme
1964_Coltrane's_Sound
1964_Crescent
1965_Ascension

而要得到A Love Supreme专辑的完整历史背景,则可以试试-C(或--context=#)选项,它可以把该专辑之前和之后的内容都组合在一起。

$ ls -1 | grep -C 2 'A_Love_Supreme'
1963_John_Coltrane_&_Johnny_Hartman
1963_Live_At_Birdland
1964_A_Love_Supreme
1964_Coltrane's_Sound
1964_Crescent

对于在一个文件或一组文件中有多个匹配结果的情况,这样的显示方式可能会有点让人困惑。例如,Coltrane发行过一些演唱会专辑(live album),如果你想看看这些专辑前后的其他专辑,将会得到更加复杂的结果。如下所示:

$ ls -1 | grep -C 1 Live
1963_John_Coltrane_&_Johnny_Hartman
1963_Live_At_Birdland
1964_A_Love_Supreme
--
1965_Last_Trane
1965_Live_in_Seattle
1965_Major_Works_of_John_Coltrane
--
1965_Transition
1966_Live_at_the_Village_Vanguard_Again!
1966_Live_in_Japan
1967_Expression
1967_Olatunji_Concert_Last_Live_Recording
1967_Stellar_Regions

以上结果中,用两个连字符(--)将每组匹配结果分隔开。前两组结果很明显(标题中包含Live的专辑在中间,在它之前和之后各有另一个专辑),但最后一组看起来要复杂得多。在标题中包含Live的多个专辑彼此紧挨着列在中间,所以结果都堆在了一起。这样的结果看起来可能会有些怪异,但是如果你看一下每个Live实例,就会注意到相关专辑之前和之后的专辑实际上都是列好的。

如果结合-n选项,结果中的信息将更有用,该选项可以列出行号(因为这里使用的是ls -1,所以它是ls结果列表中的行号)。

$ ls -1 | grep -n -C 1 Live
37-1963_John_Coltrane_&_Johnny_Hartman
38:1963_Live_At_Birdland
39-1964_A_Love_Supreme
--
48-1965_Last_Trane
49:1965_Live_in_Seattle
50-1965_Major_Works_of_John_Coltrane
--
52-1965_Transition
53:1966_Live_at_the_Village_Vanguard_Again!
54:1966_Live_in_Japan
55-1967_Expression
56:1967_Olatunji_Concert_Last_Live_Recording
57-1967_Stellar_Regions

现在通过行号数字后面的不同字符,-C选项可以为每一行提供更多的信息。冒号(:)表示该行是匹配行,而连字符(-)则表示该行是匹配行之前或之后的行。54行(1966_Live_in_Japan)具有双重作用。它在1966_Live_at_the_Village_Vanguard_Again!(这是一个匹配行)之后,因而应该用连字符;但54行本身也是一个匹配行,因而需要用冒号。因为匹配行更加重要,匹配符获胜,所以这一行最终使用了冒号。

-v

在爵士乐界,John Coltrane在其死后近40年依然占据统治地位。同样,Led Zeppelin也被认为是空前的摇滚乐巨星之一。当他们在一起时,Led Zeppelin发行了9张专辑;而其中很多专辑都采用乐队的名字来作为专辑的标题(是的,发行的第四张专辑其实没有标题,但大部分评论家仍然把这张专辑称为Led Zeppelin IV,真幽默)。如果你想查看一组包含Led Zeppelin专辑的MP3文件夹,但不包括那些标题中含有Led Zeppelin一词的专辑,该怎么做呢?使用-v(或--invert-match)选项,就可以只显示与给定模式不匹配的结果了。

$ ls -1
1969_Led_Zeppelin
1969_Led_Zeppelin_II
1970_Led_Zeppelin_III
1971_Led_Zeppelin_IV
1973_Houses_Of_The_Holy
1975_Physical_Graffiti
1976_Presence
1979_In_Through_The_Out_Door
1982_Coda
$ ls -1 | grep -v Led_Zeppelin
1973_Houses_Of_The_Holy
1975_Physical_Graffiti
1976_Presence
1979_In_Through_The_Out_Door
1982_Coda

使用-v选项,确实能够对结果进行过滤,只精确显示需要的内容。虽然-v选项并不常用,但在需要用的时候,你会庆幸有这样一个选项可以使用。

-l

grep命令能够列出包含你要搜索的内容的行,但有时可能并不需要知道这些行的具体内容,而只要知道包含这些匹配行的文件名称就可以了。在9.8节中,曾经在H.P. Lovecraft的小说中查找包含hideous的行。而用-l(或--files-with-matches)选项,就能够只列出这些行所在的文件(记住,-i选项用于让搜索不区分大小写)。

$ grep -il hideous h_p_lovecraft/*
h_p_lovecraft/Call of Cthulhu.txt
h_p_lovecraft/From Beyond.txt
h_p_lovecraft/The Case of Charles Dexter Ward.txt

当和其他命令结合使用时,这种结果会变得特别有用。例如,如果你想把包含hideous这个词的Lovecraft小说列表打印出来,可以把greplpr命令按以下方式组合起来:

$ grep -il hideous h_p_lovecraft/* | lpr

注意,这个命令打印的只是小说的名称列表,而不是小说本身(不过,有办法可以做到这一点,这里给个提示:与cat命令有关)。

grep | grep

如果要列出John Coltrane在其音乐生涯最后两年发行的专辑,该怎么办呢?非常简单。如下所示:

$ ls -1 | grep 196[6-7]

1966_Live_at_the_Village_Vanguard_Again!
1966_Live_in_Japan
1967_Expression
1967_Olatunji_Concert_Last_Live_Recording
1967_Stellar_Regions

如果将范围设置为[5-7],结果会得到比1966到1967年更长的列表。到目前为止,一切顺利,但如果不想包括任何演唱会专辑,又该怎么办呢(这通常是一个可怕的错误,但我们这里只是假设有这样的需求)?解决办法如下所示:

$ ls -1 | grep 196[6-7] | grep -v Live
1967_Expression
1967_Stellar_Regions

-v选项(在9.13节中介绍过)用于剔除包含Live的行,但这里真正有趣的地方是:如何提取ls -1的输出,再把它通过管道传递给grep 196[6-7],然后再把来自过滤器的输出通过管道传给第二个grep命令实例,正是由这个实例来使用-v Live。最终得到的就是想要的结果:所有在1966年到1967年之间发行的John Coltrane的专辑,而且在标题中没有包含Live。朋友们,从这种简单的方式中看到Linux命令行的强大威力了吧!

本章重点介绍了两个经常用到的命令:locategrep。虽然它们之间有一定关系(都能帮助Linux用户在计算机中查找文件和信息),但其工作原理有所不同。locate命令是通过一个文件名的数据库来执行对文件名的搜索,这样可以加快其运行速度;而grep命令则是实时地对文件内容进行搜索,以查找指定的搜索内容。

虽然locategrep这两个命令都很酷,但是在对文件系统进行搜索方面,它们只不过是个开始。下一章将介绍Linux系统中另一个最强大和通用的命令,它是locategrep的完美补充,实际上也能把它们串联在一起使用。什么命令呢?它就是强大的find命令。翻到下一页,让我们开始吧!



第10章 find命令

第9章介绍了用于搜索文件的locate命令和在文件中搜索数据的grep命令。Linux系统中用于搜索的三个强大命令中的第三个命令就是find。虽然locate命令搜索文件的数据库,使得它的运行速度很快,但要依赖不断更新的数据库;而find命令则可以根据指定的查询条件,轻松地对文件进行搜索。由于find命令必须分析文件的结构,所以它的速度比locate命令要慢很多,然而可以用find命令完成很多用locate命令无法实现的功能。

本章的所有例子都围绕着如何在外部硬盘上查找文件而展开,这个硬盘中包含了很多音乐文件,保存在/media/music目录下。从这些例子中可以看到,find命令能以多种方式对文件进行分析处理。

find -name

find命令的基本用途是根据文件名(或文件名的一部分)查找文件,因此也就有了-name选项。在默认情况下,find会自动递归搜索整个目录结构。要在music盘中查找唯一的Shaggs组下的所有MP3文件,如下所示:

$ cd /media/music
$ find . -name Shaggs
./Outsider/Shaggs

什么?这个结果肯定不对。find命令找到了文件夹,但没有歌曲。为什么呢?因为没有使用任何通配符,所以find命令搜索的就是名为“Shaggs”的特定文件。只有一项结果可以精确匹配这一名称:包含所要歌曲的文件夹(因为文件夹是一种特殊的文件)。

需要使用通配符,但为了防止shell以我们不期望的方式解释通配符,就必须用引号把搜索内容括起来。下面就用新改进后的方法再搜索一下:

$ find . -name  "*Shaggs*"
./Outsider/Shaggs
./Outsider/Shaggs/Gimme_Dat_Ting_(Live).mp3
./Outsider/Shaggs/My_Pal_Foot_Foot.ogg
./Outsider/Shaggs/I_Love.mp3
./Outsider/Shaggs/Things_I_Wonder.ogg

用引号把通配符括起来,就找到文件夹和文件了。

说明 正在使用find命令的另一个选项-print,可能你还没有意识到。-print选项告诉find在终端界面上列出它的搜索结果。-print选项默认是打开的,所以不需要在运行find命令时包括该选项。

find命令的另一重要方面是,搜索结果的格式取决于正在搜索的路径。前面使用的是相对路径,所以结果也是按相对路径给出。如果使用绝对路径(以/作为路径的开始),会发生什么呢?

$ find / -name "*Shaggs*"
/music/Outsider/Shaggs
/music/Outsider/Shaggs/Gimme_Dat_Ting_(Live).mp3
/music/Outsider/Shaggs/My_Pal_Foot_Foot.ogg
/music/Outsider/Shaggs/I_Love.mp3
/music/Outsider/Shaggs/Things_I_Wonder.ogg

如果搜索使用的是相对路径,那么搜索结果使用的也是相对路径;如果搜索使用的是绝对路径,那么结果使用的也会是绝对路径。从本章后面的例子中还可以看到这一原则的具体体现。现在只需要记住这一重要思想就可以了。

说明 要找到有关Shaggs的更多信息,可访问www.allmusic.com/cg/amg.dll?p=amg&sql=11:qyk9kett7q7q,或只是在www.allmusic.com中搜索“Shaggs”。

find -user

除了能通过名称来搜索文件以外,也能通过拥有者来搜索文件。想在music盘中搜索scott拥有的文件吗? 使用find命令和-user选项,后面输入用户名(或用户编号,在/etc/passwd中可以找到),如下所示:

$ find . -user scott

哇!找到的结果真多!查找拥有者不是scott的文件,可能更容易,只要在想取反的选项前面加一个叹号(!)就可以了。

说明 为了节省篇幅,这里删除了一些使用ls -l命令时通常会看到的数据。

$ find . ! -user scott
./Outsider/Wing/01_-_Dancing_Queen.mp3
$ ls -l ./Outsider/Wing/01_-_Dancing_Queen.mp3
gus music ./Outsider/Wing/01_-_Dancing_Queen.mp3

嗯……Wing的一首歌的拥有者是gus,而不是scott。为了解决这个问题,需要使用chown命令(在第7章中介绍过)。记住,叹号(!)可以作为NOT运算符使用(例如,前面的命令是在说“搜索拥有者不是scott的文件”)。

说明 Wing是位歌唱家,因在South Park中以她的名字命名的一集而广为人知,她的网站是www.wingtunes.com。强烈推荐Wing版的“Dancing Queen”,以及她演唱的“I Want to Hold Your Hand”。

file -group

如果想让一个命令可以处理多个用户,那么应该提供针对用户组的某个选项。find命令也不例外。如果想搜索特定用户组拥有的文件,只需使用-group选项,后面输入组的名称或编号。在music盘中,scott应该是拥有者,music则是它属于的用户组。让我们来看看是否有文件不属于music组,如下所示:

$ find . ! -group music
./Disco/Brides_of_Funkenstein_-_Disco_to_Go.mp3
./Disco/Sister_Sledge_-_He's_The_Greatest_Dancer.mp3
./Disco/Wild_Cherry_-_Play_That_Funky_Music.mp3
./Electronica/New_Order/Bizarre_Love_Triangle.mp3

在众多文件中,只有4个文件不属于music组。现在需要对这些文件运行chgrp命令(参见第7章),使这个盘上的所有文件属于同一用户组。

注意,这里使用叹号(!)的意思就是“搜索不属于music组的文件”。

file -size

有时,可能需要根据文件的大小查找文件,find命令可以进行这样的搜索。使用-size选项指定文件大小,在其后的数字后面加一个字符,表示使用的单位。如果不提供单位字符,则使用默认值。不过,你应该明白这可能不能搜索出你想找的内容。如果没有在数字后面附加字符,默认使用的大小单位就是512字节块(文件的字节数先除以512,再取整到最接近的一个整数)。真是太复杂的数学计算。更容易的表示方法是在数字后面使用后缀,用较为通用的单位表示文件大小,如表10-1所示。

表10-1 根据文件大小搜索文件
后  缀 含  义
b 512字节块(默认值)
c 字节(Byte)
k 千字节(Kilobyte,KB)
M 兆字节(Megabyte,MB)
G 吉字节(Gigabyte,GB)

假设我们想在Clash久唱不衰的专辑(如London Calling)中查找文件大小是10 MB的歌曲。使用find命令完成这个搜索非常简单,如下所示:

$ cd Punk/Clash/1979_London_Calling
$ find . -size 10M
./07_-_The_Right_Profile.ogg
./08_-_Lost_In_The_Supermarket.ogg
./09_-_Clampdown.ogg
./12_-_Death_Or_Glory.ogg

真奇怪,只有4首歌?使用find命令时需要注意一点:如果只说10M,那么find命令就只搜索文件大小恰好是10 MB的文件(当然是取整后的10 MB)。如果要搜索的文件大小大于10 MB,需要在指定的大小前面加一个加号(+);如果要搜索的文件大小小于10 MB,则需要在大小前面加一个减号(-),如下所示:

$ find . -size +10M
./Jimmy_Jazz.ogg
./ Lover's_Rock.ogg
./ Revolution_Rock.ogg

现在有一个问题,指定10M,搜索到的文件正好就是10 MB,而不包括那些比10 MB更大的文件;指定+10M,搜索到的文件将都超过10 MB,但不包括那些正好是10 MB的文件。如何二者兼得呢? 如果想学习如何获得大于或等于10 MB文件的方法,可以阅读10.7节。

提示 如果要搜索大型文本文件,可以在数字后面使用c。如表10-1所示,c将搜索的大小单位修改成字节。在文本文件中一个字符就是一个字节,所以记住选项c的一种简单办法就是,把它与文本文件中的“字符(character)”联系起来记忆。

例如,为搜索大文本文件,可以使用以下代码:

  $ find /home/scott/documents -size +500000c

find -type

find命令最有用的选项就是-type,用于指定想要搜索的对象的类型。还记得UNIX系统中所有东西都是文件吧(在1.1节介绍过),所以其实指定的就是要搜索的文件的类型。表10-2列举了find命令中可以使用的文件类型。

表10-2 根据文件类型搜索文件
文件类型字符 含  义
f普通文件
d目录
l符号(软)链接
b块文件
c字符文件
pFIFO(First In First Out)文件
s套接字

假设要快速查找music盘中Steely Dan的所有专辑,因为所有歌曲文件都是按专辑存放到不同的目录中,所以可以使用-type d搜索文件夹:

$ cd /media/music/Rock
$ find Steely_Dan/ -type d
Steely_Dan/
Steely_Dan/1980_Gaucho
Steely_Dan/1975_Katy_Lied
Steely_Dan/1974_Pretzel_Logic
Steely_Dan/1976_The_Royal_Scam
Steely_Dan/2000_Two_Against_Nature
Steely_Dan/1972_Can't_Buy_A_Thrill
Steely_Dan/1973_Countdown_To_Ecstasy
Steely_Dan/1977_Aja

这样列出结果挺有用,但因为每个专辑目录的名称都以其发行年份作为开始,这样就可以根据年份进行排序,以便得到更精确的信息,如下所示:

$ cd /media/music/Rock
$ find Steely_Dan/ -type d | sort
Steely_Dan/
Steely_Dan/1972_Can't_Buy_A_Thrill
Steely_Dan/1973_Countdown_To_Ecstasy
Steely_Dan/1974_Pretzel_Logic
Steely_Dan/1975_Katy_Lied
Steely_Dan/1976_The_Royal_Scam
Steely_Dan/1977_Aja
Steely_Dan/1980_Gaucho
Steely_Dan/2000_Two_Against_Nature

使用管道符(|)对find命令生成的列表进行过滤,效果非常好。随着你越来越能熟练使用find命令,你会发现用它能够完成的功能也越来越多。

find -a

find命令的一个重要特点就是能够把多个选项组合起来,以便更严格地定义搜索条件。可以用-a(或-and)把任意多个选项串接在一起。例如,如果要找出世界上最棒的摇滚乐队Rolling Stones演奏的每首歌曲,你可能会使用-name "Rolling_Stones*",但这样做并不一定奏效。有些文件夹的名称中可能有Rolling_Stones,也可能有一两个带有乐队名称的软链接,因此,还需要使用-type f。把这两个条件按以下方式组合起来:

$ find . -name "Rolling_Stones*" -a -type f
1968_Beggars_Banquet/03_-_Dear_Doctor.mp3
1968_Beggars_Banquet/01_-_Sympathy_For_The_Devil.mp3
1968_Beggars_Banquet/02_-_No_Expectations.mp3
1968_Beggars_Banquet/04_-_Parachute_Woman.mp3
1968_Beggars_Banquet/05_-_Jig-Saw_Puzzle.mp3
1968_Beggars_Banquet/06_-_Street_Fighting_Man.mp3

结果够酷吧!那么我们有多少首Rolling Stones乐队的歌曲呢?把find命令的结果通过管道传递给wc(代表word count),但还要使用-l选项,这样最终结果统计的是行数,而不是字数,如下所示:

$ find . -name "Rolling_Stones*" -a -type f | wc -l
317

317首Rolling Stones乐队的歌曲。真太棒了!在这个例子中,你总是能够得到想要的东西①。

① 此句话的原文是“..., you can always get what you want.”——译者注


说明 如果你对最后一句话的含义没什么感觉的话,可以现在就亲自聆听一下Let It Bleed这张专辑。别忘了把声音开大点。

find -o

在10.4节中,可以使用find命令,把London Calling中大小为10 MB的Clash的所有歌曲文件都找出来,而且也可以使用find命令把London Calling中大小超过10 MB的歌找出来,但是用-size选项不能二者兼得。在10.6节中,-a选项使用AND来组合选项。不过,也可以使用-o(或-or)选项,用OR来组合各个选项。

因此,为了从London Calling中查找文件大小等于或大于10 MB的歌曲文件,可以使用以下命令:

$ cd London_Calling
$ find . -size +10M -o -size 10M
03_-_Jimmy_Jazz.ogg
07_-_The_Right_Profile.ogg
08_-_Lost_In_The_Supermarket.ogg
09_-_Clampdown.ogg
12_-_Death_Or_Glory.ogg
15_-_Lover's_Rock.ogg
18_-_Revolution_Rock.ogg
(25th_Anniversary)_-_18_-_Revolution_Rock.mp3
(25th_Anniversary)_-_37_-_Heart_And_Mind.mp3
(25th_Anniversary)_-_ 39_-_London_Calling_(Demo).mp3

哎呀,搜索结果中还包括London Calling的25周年纪念版,我们不想要这个。现在需要做两件事:一是从结果中排除25周年纪念版的信息,二是确保OR语句运行正常。

要排除25周年纪念版的歌曲,在命令行的末尾增加!-name "*25*"。要确保OR语句运行正常,需要用括号把它括起来,对语句进行组合。不过,需要使用反斜杠(\)对括号进行转义,这样shell才不会误解它们,而且还需要在语句前后都留下空格。组合后的命令如下所示:

$ cd London_Calling
$ find . \( -size +10M -o -size 10M \) ! -name
"*25*"
03_-_Jimmy_Jazz.ogg
07_-_The_Right_Profile.ogg
08_-_Lost_In_The_Supermarket.ogg
09_-_Clampdown.ogg
12_-_Death_Or_Glory.ogg
15_-_Lover's_Rock.ogg
18_-_Revolution_Rock.ogg

好极了。有7首大小等于或超过10 MB的歌。

说明 有关London Calling的更多信息,可以访问Allmusic.com(www.allmusic.com/cg/amg.dll?p=amg&sql=10:aeu1z82ajyvn)或Pitch- fork (www.pitchforkmedia.com/record-reviews/c/clash/london-calling.shtml)。

也可以用-o找出music盘中有多少首歌。从/media/music的根目录下运行命令,并使用-a选项(耐心点,一会就要使用-o了),如下所示:

$ find . -name "*mp3*" -a -type f | wc -l
23407

23 000个结果? 结果不对。刚才只搜索了mp3文件,而大量歌曲是以更高级的Ogg Vorbis格式编码的(这是一种可以取代mp3的、专利免费的开源格式)。搜索mp3或ogg文件,并用wc l统计结果,如下所示:

$ find . \( -name "*mp3*" -o -name "*.ogg*" \) -a
➥-type f | wc -l
41631

看起来好多了,但还有一些FLAC文件没有包括进来。再添加一个-o选项,如下所示:

$ find . \( -name "*mp3*" -o -name "*.ogg*" -o -name
➥"*.flac*" \) -a -type f | wc -l
42187

现在得到的结果更棒了:有42 000首歌,而且数量还在增长。

find -n

本章前面几节使用叹号(!)来对表达式进行取反(参见10.2节和10.7节),现在进一步看看这个操作符。在10.7节中,使用find命令确定在music盘中有多少个mp3、ogg和flac文件,但这个盘中总共有多少个文件呢?

$ find . -name "*" | wc -l
52111

嗯,总共有52 000多个文件,但其中只有42 000个是mp3、ogg或flac之类的文件,其他是什么文件呢?再创建一个find命令,将以.mp3、.ogg或.flac为后缀的文件,还有目录排除掉。用括号把这4个条件括起来,然后在命令前面放一个叹号(!),表示要否定这些条件,实际要搜索的是与这些指定的条件不匹配的结果,如下所示:

$ find . ! \( -name "*mp*" -o -name "*ogg" -o
➥-name "*flac" -o -type d \)
./Folk/Joan_Baez/Joan_Baez_-_Imagine.m3u
./500_Greatest_Singles/singles.txt
./Blues/Muddy_Waters/Best_Of.m3u
./Blues/Robert_Johnson/Hellhound_On_My_Trail.MP3
./Blues/Johnny_Winter/Johnny_Winter.m3u [Results
truncated for length]

现在找到答案了,或者至少正在获得答案。结果中有m3u(播放目录)文件、文本文件以及后缀是MP3而不是mp3的文件(还有JPEG、GIF和音乐视频文件)。和所有Linux命令一样,find命令也是区分大小写的(如第1章所述),所以搜索*mp3*时,并不会搜索到后缀为.MP3的文件。有什么又快又简单的办法能够修改这些文件,让它们的扩展名都变成正确的小写格式呢?当然有,还是用find命令完成这个任务。

find -exec

现在进入find命令真正展露其威力的领域:能够对搜索到的文件再执行命令。在列出用于限制搜索的选项(例如-name-type-user)后,再附加一个-exec选项,后面跟上要为找到的每个文件执行的命令。用{}符号来代表找到的每个文件,并用\对分号(;)进行转义以作为命令的结束,这样shell就不会把它理解为命令栈的标志(有关命令栈,可以参见第4章中的相关介绍)。

例如,上一节中发现music盘里的某些文件是以MP3结尾的。因为更喜欢小写字母的扩展名,所以需要把所有出现MP3的地方都转换成mp3,保持文件的统一性。用find命令的-exec选项完成这个操作。首先,先确保选中的文件是以MP3结尾的,如下所示:

$ find . -name "Robert_Johnson*MP3"
./Blues/Robert_Johnson/Judgment_Day.MP3
./Blues/Robert_Johnson/Dust_My_Broom.MP3
./Blues/Robert_Johnson/Hellhound_On_My_Trail.MP3

接下来,用带有-exec选项的find命令修改文件扩展名。和-exec选项一起使用的程序是rename,它可以修改文件名中的某些部分:

$ find . -name " *MP3" -exec rename 's/MP3/mp3/g' {} \;

rename命令后面是关于文件名如何变化的指令,其格式为:s/old/new/gs代表substitute(代表),g代表global(目标)]。现在来看看命令能否起到作用:

$ find . -name "Robert_Johnson*MP3"
$ ls -1 Blues/Robert_Johnson/
Hellhound_On_My_Trail.mp3
Judgment_Day.mp3
Dust_My_Broom.mp3
Love_in_Vain.mp3
Me_and_the_Devil_Blues.mp3

这个命令看起来是奏效了。接下来试试另一种情况下的类似处理。在上一节的搜索结果中有很多m3u(或播放列表)文件。遗憾的是,这些文件的文件名中很多都有空格,这是要尽量避免的。首先,先找到文件名中有空格的m3u文件列表。搜索* *m3u,也就是用两个通配符把一个空格围起来,就能找到文件名中包含空格的文件,如下所示:

$ find . -name "* *m3u"
./Christmas/Christmas With The Rat Pack.m3u
./Christmas/Holiday_Boots_4_Your_Stockings.m3u
./Classical_Baroque/Handel/Chamber Music.m3u
./Classical_Opera/Famous Arias.m3u
./Doo_Wop/Doo Wop Box.m3u
./Electronica/Aphex_Twin/I Care Because You Do.m3u

现在,先搜索一下文件名中有空格的m3u文件,当找到这样的文件时,就能够对它运行rename命令。在此,为了修复这个问题,用_来取代\ (需要对空格进行转义,这样find命令才能够明白要搜索的内容,而shell也不会混淆各个命令),如下所示:

$ find . -name "* *m3u" -exec rename 's/\ /_/g' {}
\;
$ find . -name "* *m3u"
$

命令像所期待的那样运行。

说明 注意,在对文件执行命令之前,必须先明确要修改哪些文件。这是一种谨慎处事的好习惯,谁都不想修改错误的文件!

find -fprint

前面每次使用find命令时,都没有使用-print选项,因为这个选项默认就是打开的。如果要把搜索结果打印到一个文件,而不是终端显示界面,则可以使用-fprint选项,后面跟上想要创建的文件名。如下所示:

$ find . ! \( -name "*mp*" -o -name "*ogg" -o -name
➥"*flac" -o -type d \) -fprint non_music_files.txt
$ cat non_music_files.txt
./Folk/Joan_Baez/Joan_Baez_-_Imagine.m3u
./500_Greatest_Singles/singles.txt
./Blues/Muddy_Waters/Best_Of.m3u
./Blues/Robert_Johnson/Hellhound_On_My_Trail.MP3
./Blues/Johnny_Winter/Johnny_Winter.m3u

然后,就可以在脚本中使用这些结果,或把它们发送到打印机(如果有帮助的话)。

如你所见,用find命令能够做很多事情。但find命令还能够完成更多的操作,所以真应该查看一下man find页面,或是在互联网上阅读一些相关的优秀教程,了解这一命令其他更神秘但也很有用的功能。通过指定各种选项,find命令可以搜索并列出满足条件的文件和文件夹。但find命令真正可圈可点的地方,是可以使用-exec选项为找到的文件执行命令,以及把它的输出通过管道发送给其他命令。find命令是Linux系统中功能最强大的命令,请立即把它用起来吧!



第四部分 环境

第11章 shell

第12章 监视系统资源

第13章 安装软件



第11章 shell

到目前为止,本书介绍的都是在bash shell中运行各种命令,一直没有关注shell本身。本章将介绍两个影响shell使用的命令:historyalias,前者可以列出所有在命令行中输入的命令,后者可以为命令创建快捷方式。这两个命令很有用,能够在使用命令行时为你节省大量时间。对于计算机用户,懒惰并不是件坏事。在使用Linux系统时,这两个命令一定能尽可能地满足用户懒惰的要求。

history

每当在shell中输入命令时,命令就会保存在主(home)目录中一个名为.bash_history的文件里(文件名之前的点号表示这是一个隐藏文件,只有用ls -a才能显示出来)。在默认情况下,这个文件只保存最新的500条命令行历史记录。要查看命令行历史列表,只需输入history命令。如下所示:

$ history 

  496 ls
  497 cd rsync_ssh
  498 ls
  499 cat linux
  500 exit

因为正在查看的命令可能会有500条,它们会快速地闪过屏幕,在到达最后一条记录之前,快得看不清任何内容。想要一次只查看一个屏幕的内容,就得靠老朋友less命令了。如下所示:

$ history | less

现在就能非常容易地逐一查看结果了。

警告 现在应该明白为什么在命令行中输入密码以及其他敏感信息时,需要谨慎的原因了吧:任何能够查看.bash_history文件的人,都能够看到那些密码。小心些,仔细想想在命令行中直接输入了什么!

!!

如果想再次运行刚刚使用过的那条命令,只要输入两个叹号(!!),就能查看命令历史文件,并运行其中的最后一条命令。如下所示:

$ pwd
/home/scott
$ !!
pwd
/home/scott

注意,先看到的是将要运行的实际命令,接着看到的是该命令的执行结果。让计算机做些乏味的工作,这是个非常有用的办法。

![##]

当运行history时,它自动在每条命令前加一个数字。如果想运行前面的某条命令,而且也知道history赋予它的数字,这时只需要在感叹号后面跟上命令的历史编号,就能再次运行这个命令。如下所示:

$ pwd
/home/scott
$ whoami
scott
$ !499
pwd
/home/scott

如果不确定这个数字是多少,再运行history命令找找看。要注意的是,在这个例子中,pwd命令第一次的历史数字是499,但在用!499再次运行这个命令后,它就变成了498,因为新命令把它在列表中向下推了一位。

![string]

能够通过引用命令的历史编号来再次运行它,当然不错。但这需要事先知道命令在history中的编号,而查找这个编号多少有点麻烦(虽然把history的输出通过管道传递给grep命令可能会有帮助,但仍然不是最好的办法)。为了引用以前输入的命令,通常更好的办法是按照命令的实际名称来引用。如果在感叹号后面输入某个命令的前几位字母,shell将运行它能够在.bash_history文件中找到的与之匹配的第一个命令。

$ cat /home/scott/todo
Buy milk
Buy dog food
Renew Linux Magazine subscription
$ cd /home/scott/pictures
$ !cat
cat /home/scott/todo
Buy milk
Buy dog food
Renew Linux Magazine subscription

如果在命令历史中找到3个cat命令的运行记录,即35(cat /home/scott/todo)、412(cat /etc/apt/sources.list)和496(cat /home/scott/todo),而此时输入!cat,就会运行编号为496的那条命令。如果要运行编号为412的命令,要么直接运行!412,要么在感叹号后面提供足够多的信息,让命令可以判断出要引用的是编号为412的命令。

$ !cat /etc
cat /etc/apt/sources.list
deb http://us.archive.ubuntu.com/ubuntu breezy main restricted
deb-src http://us.archive.ubuntu.com/ubuntu breezy
➥main restricted

因为人类记忆单词要比记忆数字容易得多,所以最终可能还是采用通过字符串的方法来调用以前运行过的命令。如果觉得不确定,可以运行history命令看一看。

alias

如果经常使用某个命令,或者是某个命令特别长、很难拼写,那么为这个命令起个别名,是很值得的。在创建别名以后,只要输入别名,就可以运行它所引用的命令。当然,如果一个命令特别复杂,或者包括好多行,则应该把它转换成脚本或者函数。不过对于小事情来说,别名也算是完美的。

所有别名都保存在主目录的一个文件中,可能会在.bashrc中找到它们,但更可能会在.bash_aliases中找到它们。大多数Linux分发版会提供几个事先定义好的别名,要查看这些已有的别名,只需要在命令行中输入alias。如下所示:

$ alias
alias la='ls -a'
alias ll='ls -l'

大多数Linux分发版尽可能减少默认别名的数量,增加新的别名就得靠你自己了,稍后就会看到。

alias [alias name]

在定义了多个别名后,输入alias命令,可能很难找到特定的某个别名。如果想查看一下特定别名的命令内容,只需要在alias命令后面加上这个别名的名字就可以了。如下所示:

$ alias wgetpage
alias wgetpage='wget --html-extension --recursive
➥--convert-links --page-requisites --no-parent $1'

现在,wgetpage别名到底做了什么,一目了然,既快又简单。

说明 第15章将会详细介绍wget命令。

alias [alias]= '[command]'

如果你发现自己正在反复地输入一个命令,或许这时就应该为它创建一个别名了。例如,要只查看当前工作目录中的子目录,需要使用ls -d */命令。为这个命令创建一个临时的别名,如下所示:

$ ls -d */
by_pool/  libby_pix/  on_floor/
$ alias lsd='ls -d */'
$ lsd
by_pool/  libby_pix/  on_floor/

用这种方法使用alias时,应该注意几点内容。别名中不能包含等号(=),在定义别名时,会在名称后面立即跟上一个等号。不过,在别名真正的内容中可以使用等号。此外,用这种方法创建的别名只有在当前shell会话(session)有效时才能存在。会话退出后,别名也就不存在了。

想创建会话退出后仍然还能存在的别名吗?请阅读下一节内容。

alias [alias name]= '[command]'

如果想让别名能够在不同的shell会话中使用,就需要把它们添加到shell用来保存别名的文件中。大多数情况下,这个文件不是.bashrc,就是.bash_aliases。本例中使用的是.bash_aliases。不管是哪个文件,编辑它们时要十分谨慎,否则,以后登录时可能会造成麻烦。可以在编辑文件之前先做个备份,有备无患。

说明 如何找出应该使用哪个文件来保存别名?很简单:输入ls -a ~。如果在结果中看到.bash_aliases,那就使用这个文件;否则,检查一下.bashrc,看看里面是否定义了其他别名。如果在这个文件中没有看到任何别名,那就再看看.profile,偶尔也会用到它。

.bash_aliases中增加新的别名,先用你喜欢的文本编辑器打开它,然后增加如下所示的一行:

alias lsd='ls -d */'

11.7节中讨论的规则在这里也同样适用:别名的名称中不能包含等号(=)。在.bash_aliases中增加别名以后,保存文件并退出。但新增加的别名不能用。需要重新加载.bash_aliases(或.bashrc,如果使用的是这个文件),新增加的别名才会生效。有两种办法:第一种办法是退出shell,再登录进来,但这种办法并不好用,不推荐使用;第二种是通过运行命令来重新加载文件,如下所示。

$ . .bash_aliases

先是一个点号(.),后面跟着一个空格,之后是文件名(该文件名以点号作为开始)。现在新加的别名就能够运行了。因为每次增加新的别名后都需要重新加载别名文件,所以一次多增加几个别名,是个好主意。

unalias

天下无不散的宴席,别名也会有不再需要使用的时候。可以使用unalias命令删除别名。

$ ls -d */
by_pool/  libby_pix/  on_floor/
$ alias lsd='ls -d */'
$ lsd
by_pool/  libby_pix/  on_floor/
$ unalias lsd
$ lsd
$

但要注意,正如11.7节所述,这个命令也是永远只对临时的shell 别名有效。前面例子中的lsd别名被删除了。如果对.bash_aliases文件中的别名使用unalias命令,也会删除相应的别名,但前提是一直保持shell登录状态。在退出shell,再登录回来,或是重新加载.bash_aliases时,原来删除的别名依旧存在。

要从.bash_aliases中删除别名,需要编辑这个文件,手工删除包含别名的相应行。如果觉得以后有可能还会使用某个要删除的别名,就在这个别名的前面放一个英磅符号(#),表示注释掉它,如下所示:

# alias lsd='ls -d */'

保存.bash_aliases文件,再用. .bash_aliases命令重新加载它,注释掉的别名就不再起作用了。但如果以后又需要使用它了,打开.bash_aliases文件,删除前面的英磅符号,保存并重新加载这个文件,就又能使用这个别名了。

作为Linux用户,应该尽量减少为了完成目标而必须输入的字符数量。Unix最初的开发人员就是这一理念的忠实信徒,这就是要用ls代替list,用mkdir代替makedirectory的原因。

本章介绍的两个命令是historyalias,两者都有助于在Linux中体现这一理念——减少为了完成目标而必须输入的字符数量。厌烦了输入又长又复杂的命令?可以用history命令引用它,或为它创建一个别名。无论哪一种方法都能为你节省一些按键的劳动,而你的键盘(更重要的是你的指头和手腕)也会因此而感谢你的。



第12章 监视系统资源

优秀的计算机用户也得担负起系统管理员的部分职责,不断地查看计算机的健康情况,确保系统的每一部分都在稳定地运行。为了实现这个目标,Linux提供了几个用于监视系统资源的命令。本章将介绍其中的几个命令。这些命令中的大多数都具有多种用途,但都各自擅长监视系统中的某一特定方面。这些任务包括跟踪计算机中正在运行的程序(pstop)、终止出错的进程(kill)、列出所有打开的文件(lsof)、报告系统内存的占用情况(free)和磁盘空间的占用情况(dfdu)。学好这些工具,你会发现在很多场合下它们都能派上用场。

ps aux

每当正在运行的某个程序发生死锁,并停止响应时,你就得手工关闭它;或者,你可能想知道某个用户正在运行什么程序;又或者,你只可能想知道自己的计算机上当前正在运行的进程的一些情况。对于上述这些情况,以及其他很多情况,解决的方法就是使用ps命令列出计算机上当前打开的进程。

遗憾的是,ps命令有很多版本,它们各自的选项种类也各不相同。甚至根据是否在这些选项前面加了连字符(-),它们的含意也各不相同,例如u-u就代表完全不同的两件事。到目前为止,本书都非常严格地在所有选项前面加了连字符(-),但是对于ps命令,为了让操作更加容易和统一,它的选项就不要求使用连字符。

要查看系统中用户正在运行的所有进程,可以在ps命令后面使用以下选项:a(表示所有用户)、u(以面向用户的格式显示,或显示拥有每个进程的用户)、x(没有控制tty①或终端屏幕的进程,“显示每个进程”的另一种方法)。预先提示一下,屏幕上将会飞驰而过相当长的列表内容: 这台计算机上会显示132行。

① tty通常用于指各种类型的终端设备。——译者注

$ ps aux
USER    PID %CPU %MEM    VSZ    RSS TTY    STAT  START
➥TIME COMMAND

scott 24224  4.1  4.2 1150440 44036    ?   R     11:02
➥12:03 /home/scott/.cxoffice/bin/wine-preloader

scott  5594  0.0  0.1    3432  1820 pts/6  S+    12:14
➥0:00 ssh scott@humbug.machine.com

scott 14957  0.3  7.5  171144 78628    ?   Sl    13:01
➥0:35 /usr/lib/openoffice2/program/soffice.bin
➥-writer

scott 12369  0.0  0.0       0     0    ?    Z    15:43
➥0:00 [wine-preloader] <defunct>

scott 14680  0.0  0.1    3860  1044 pts/5  R+    15:55
➥0:00 ps aux

ps命令能提供很多信息,包括进程的拥有者、唯一的进程ID编号(PID,用于标识进程)、进程正在使用的CPU百分比(%CPU)和内存百分比(%MEM)、进程的当前状态(STAT)及进程的名称。

STAT列用不同的字母表示进程的状态,最重要的一些状态如表12-1所示。

表 12-1
STAT 字母 含  义
R 运行(running)
S 休眠(sleeping)
T 停止(stopped)
Z 僵尸②(zombie)

② 表示该进程已经终止,但其父进程却无法正常终止它,造成“僵尸”进程的状态。——译者注

Z是个坏消息,因为它意味着进程基本上一直处于挂起状态,不能停止(幸运的是,这并不意味着没有解决问题的办法)。如果某个程序有问题,ps标明其状态为Z,这时或许只有重启计算机才能完全关闭这个程序。

因为ps aux提供了很多数据,所以要找到正在搜索的那个程序可能有点难。

ps aux的输出通过管道传递给grep,这是一种对特定命令的结果进行限制的简单方法。

$ ps aux | grep [f]irefox
scott  25205  0.0   0.0   4336      8  ?     S
➥Feb08  0:00  /bin/sh /opt/firefox/firefox

scott  25213  1.1  10.9 189092  113272 ?     Rl
➥Feb08 29:42  /opt/firefox/firefox-bin

现在知道了正在计算机上运行的Firefox实例,包括谁在运行这个程序、加载这个程序占用了计算机的多少资源,以及这个程序已经运行了多长时间。多么有用的信息!

提示 为什么搜索的是[f]irefox,而不是firefox呢?可以在9.11节找到答案。

ps axjf

在Linux世界中,进程并不是凭空出现的。通常,启动一个程序的同时,也会启动其他程序。例如,Linux系统中的所有进程最初都来自init,它是所有进程的源头,其PID总为1ps命令能够提供该进程树的一种基于文本的表现方式,这样就能可视化地看看该进程又创建了哪些其他进程。要查看进程树,除了使用上一节用过的aux选项,还要加上个f(其名称源于ASCII art forest)选项。

说明 通常会看到以下各列内容:

  PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND

为了让命令树更易于理解,下列代码列表删除了用ps axuf命令实际会看到的大部分列。

$ ps axuf
PPID   PID   COMMAND 
    1  7558  /usr/sbin/gdm
 7558  7561   \_ /usr/sbin/gdm
 7561  7604       \_ /usr/X11R6/bin/X :0
 7561  8225       \_ /bin/sh /usr/bin/startkde
 8225  8279           \_ /usr/bin/ssh-agent
 8225  8341           \_ kwrapper ksmserver
    1  8316  kdeinit Running...
 8316 10842  \_ konqueror [kdeinit] --silent
 8316 29663  \_ quanta
 8316 30906  \_ /usr/bin/kwrite /home/scott/analysis
 8316 17893  \_ /usr/lib/opera/9.0-20060206.1/opera
17893 17960  |   \_ /usr/lib/opera/pluginwrapper
17893 17961  |   \_ /usr/lib/opera/plugincleaner

注意,ps axjf命令引入了一个重要的新列:PPIDPPID(Parent Process ID,父进程ID编号)是创建PID进程的进程的PID编号。有了PID或PPID,就能够终止任何失控(runaway)的进程,将在12.4节中进行介绍。

ps U [username]

到目前为止,已经看到了如何用ps命令列出系统中的所有进程。如果想把结果限制为某个用户拥有的进程,只需使用U选项,后面跟上用户名或ID编号就可以了。

$ ps U scott
  PID  TTY     STAT  TIME  COMMAND 
14928  ?       S     0:00  /opt/ooo2/program/soffice-writer
14957  ?       Sl    0:42  /opt/ooo2/program/soffice.bin-writer
 4688  pts/4   S+    0:00  ssh scott@humbug.machine.com
26751  ?       Z     0:00  [wine-preloader] <defunct>
27955  pts/5   R+    0:00  ps U scott

当然,ps命令不会在结果列表中包括用户名,因为它已经是命令的一部分了。

提示 记住,如果不知道用户的ID编号,只需查看/etc/passwd。找到用户名,用户ID编号就在第三列。

kill

有时程序也会淘气,不响应正常的关闭请求。在GUI(图形用户界面)中,多次点击Close按钮,但没有任何事情发生;或是在shell中按Ctrl+C键,也没能停止正在运行的命令。当发生这种问题时,就需要使用kill命令了。

kill命令能够向进程发出多种信号,例如,“请关闭进程,同时清理占用的东西”、“请尽可能快地关闭进程”以及“马上关闭”。下面将主要介绍这三种重要的信号。当使用kill命令时,可以用一个数字或单词来指定关闭进程的紧急程度,如表12-2所示。

表12-2 与kill命令关联的常用信号
信号编号 信号单词 含  义
-1 -HUP (hang up,挂起) 控制进程已经死了,关闭进程(如果用于系统服务,会导致重新加载配置文件并重启)
-15 -TERM 正常中止进程,清除将要失控的进程和文件
-9 -KILL 停止任何正在进行的处理,马上关闭

通常,应该先试试-15(其实,如果kill命令没有使用任何选项,它就是默认值)。这样,程序就可以关闭依赖于它的任何其他程序,关闭由它打开的文件,等等。在耐心地等待了一会儿后(当然“一会儿”有多长,完全是主观的),进程仍然在完全不受控制地运行,或者仍然没有响应,这时就可以使出更大的杀手锏,使用-9选项。-9选项相当于强制中断一个正在运行的进程(即使这个进程还在处理它的作业),从而可能在系统中遗留下失去控制的临时文件或进程,所以这绝对不是个好想法。

-1(或-HUP)选项,主要用于Samba或无线连接之类的服务。虽然这个选项可能不经常用,但应该了解它的含义。

如果gvim看起来在系统中不动了(通常,它都运行得非常好,这里只是为了做个演示),可以进行以下处理:

$ ps U scott
  PID  TTY   STAT  TIME  COMMAND 
14928  ?     S     0:00  /opt/ooo2/program/soffice-writer
14957  ?     Sl    0:42  /opt/ooo2/program/soffice.bin-writer
4688   pts/4 S+    0:00  ssh scott@humbug.machine.com
26751  ?     Z     0:00  [wine-preloader] <defunct>
27921  ?     Ss    0:00  /usr/bin/gvim
27955  pts/5 R+    0:00  ps U scott
$ kill 27921
$ ps U scott
  PID  TTY   STAT  TIME  COMMAND
14928  ?     S     0:00  /opt/ooo2/program/soffice-writer
14957  ?     Sl    0:42  /opt/ooo2/program/soffice.bin-writer
4688   pts/4 S+    0:00  ssh scott@humbug.machine.com
26751  ?     Z     0:00  [wine-preloader] <defunct>
27955  pts/5 R+    0:00  ps U scott

要关闭gvim,首先用ps命令找到gvim的PID,在这个例子中是27921。然后关闭那个PID(记住,kill命令默认使用TERM信号),并用ps命令再检查一下。好的,gvim已经不在了。

说明 为什么不关闭PID 26751这个进程呢(它的STATZ,表明该进程处于“僵尸”状态)?因为即使-9选项对“僵尸”进程也没有效果,这样的进程早已经死掉了,因此kill命令对它也无能为力。重启是唯一能够解决这种问题的办法,不过这样的问题通常是无关紧要的。

top

偶尔你会发现Linux计算机突然慢了下来,但从表面却看不出任何原因。可能某个程序正在系统中全速运行,它占用了大量的处理器资源。也可能你启动了一个命令,却发现它占用的CPU处理周期比原本想象的要多得多。要找出导致问题的原因,或者只是想看看系统中有什么程序正在运行,可以使用ps命令,美中不足的是ps不能自己更新信息。其实ps命令提供的只是系统进程的快照而已。

另一方面,top命令提供的则是进程的动态更新的视图,展示系统中正在运行着什么进程,以及每个进程正在使用多少系统资源。很难在书本中体现这种动态效果,因为看不到top命令是如何每隔一秒来更新其显示的,但以下是top命令在其运行的某一时刻显示的内容:

$ top
top - 18:05:03 up 4 days, 8:03, 1 user, load
➥average: 0.83, 0.82, 0.97
Tasks: 135 total, 3 running, 126 sleeping, 2
➥stopped, 4 zombie
Cpu(s): 22.9% us, 7.7% sy, 3.1% ni, 62.1% id,
➥3.5% wa, 0.1% hi, 0.7% si
Mem:   1036136k total, 987996k used, 48140k free, 27884k buffers
Swap:  1012084k total, 479284k used, 532800k free, 179580k cached
PID    USER  PR  NI  VIRT   RES   SHR S %CPU  %MEM   TIME+
➥COMMAND
25213  scott 15  0   230m  150m   15m S 11.1  14.9  33:39.34 
➥firefox-bin
 7604  root  15  0   409m  285m  2896 S 10.8  28.2  749:55.75 
➥Xorg
 8378  scott 15  0  37084   10m  7456 S  1.0   1.1   13:53.99 
➥kicker
 8523  scott 15  0  69416   13m  3324 S  0.7   1.3   63:35.14 
➥skype
29663  scott 15 0   76896   13m  4048 S  0.7   1.3   13:48.20 
➥quanta

top命令输出的前5行显示了大量的系统信息,之后则逐行列出每个正在运行的进程。注意,top命令会自动根据%CPU列的数值对输出进行排序,所以当程序占用的处理器资源变得更多或更少时,它们在top命令列表中的位置也会发生相应变化。

如果想在top命令中关闭程序,只需按k键。这时在列表上方(在以Swap:开始的那一行的后面),会看到以下内容:

PID to kill:

输入想关闭进程的PID(假设为8026),按Enter键,命令会询问要使用什么信号编号(12.4节中讨论过这一问题):

Kill PID 8026 with signal [15]:

top命令默认使用的信号是15。如果乐意用这个默认值,就按Enter键;如果不乐意,就直接输入想使用的信号编号,然后按Enter键。一两秒钟以后,要关闭的进程就会从top显示中消失了。

q键;退出top命令。

top命令非常有用,你将发现自己会经常使用这个命令找出Linux计算机中到底正在运行着什么进程。当对计算机的运行情况有疑问时,top命令也经常能够提供答案。

lsof

在第1章中讨论过,Linux系统中的所有东西都是文件,包括目录、网络连接和设备。这意味着,即使在你看来只打开了一个文件(例如,写给妈妈的一封信),而实际上系统在这个时候打开了几千个文件。确实如此,几千个文件! 要查看完整的打开文件列表,可以使用lsof命令(list open files的缩写)。

实际上不应该这么做。如果只运行lsof命令(要得到全部的结果列表,需要以root用户来运行),得到的输出列表将包含几千个文件的信息。在这个系统中有5 497个文件正在打开和使用中。不过,为了在任何时候能够了解计算机中有多少个文件正在使用,lsof仍然不失为一种好办法。

lsof的输出通过管道传递给less命令,则可以一次只查看一屏的结果。

# lsof | less
COMMAND PID  USER  FD   TYPE DEVICE  SIZE  NODE  NAME
init      1  root  cwd  DIR  3,1      656     2  /
init      1  root  rtd  DIR  3,1      656     2  /
init      1  root  txt  REG  3,1    31608  2072  /sbin/init

仍然是5 497个结果,分成了许多屏,得翻页查看。也可以把lsof的输出通过管道传递给grep命令。不过,在接下来的几节中可以看到,lsof命令就提供了过滤掉你不想看到的数据的方法,只显示所有打开文件中让你感兴趣的那部分。

lsof -u

如果想查看由某个特定用户打开的文件(记住,文件也包括网络连接和设备等好多东西),可以在lsof命令中增加一个-u选项,后面跟上用户名(记住,lsof必须以在root用户来运行)。

说明 为了节省篇幅,这里以及后面的几个例子中删除了一些运行lsof命令时通常会看到的数据。

# lsof -u scott
COMMAND    PID   USER   NAMEP
evolution 8603  scott  /home/scott/.evolution/
➥addressbook/local/system/addressbook.db
opera    11638  scott  /usr/lib/opera/9.0/opera
opera    11638  scott  /usr/share/fonts/truetype/
➥msttcorefonts/Arial_Bold.ttf

即使过滤掉其他所有用户,只留下一个(scott),在列表中依然得到3 039行结果。但一些有趣的东西还在结果中。首先,结果表明Evolution(一个电子邮件和个人信息管理器程序)一直在运行,或者你就不知有这个程序,或者你还没有察觉到它。此外,Opera Web浏览器也在运行,这是可以预见的;而浏览器打开的一个网页需要使用Arial Bold字体文件,当然还需要其他文件。

如果你管理着一台供多人使用的计算机,可以为你的用户试试lsof -u命令。或许你会发现他们正在运行一些他们不应该运行的程序。如果你是Linux计算机的唯一用户,也可以自己试试lsof -u,或许会发现系统正在运行一些你不知道的程序。

lsof [file]

在上一节中,你看到了特定的用户打开了什么文件。现在反过来,看看特定的文件正在由谁使用。为此,只要在lsof后面输入文件在系统中的路径就可以了。例如,让我们来看看谁正在使用SSH守护进程(daemon),远程连接到这台计算机的用户会使用该进程(记住,必须以root用户来运行lsof)。

# lsof /usr/sbin/sshd
COMMAND   PID   USER  TYPE  NAME
sshd     7814   root   REG  /usr/sbin/sshd
sshd    10542   root   REG  /usr/sbin/sshd
sshd    10548  scott   REG  /usr/sbin/sshd

这就是想要的结果: 两个用户,即root和scott。如果这里出现了意想不到的用户,如4ackordood,就说明你的系统被盗用了。

说明 是的,从我们的角度来看,sshd只是一个程序;但对于Linux来说,/usr/sbin/sshd只不过是一个文件。

lsof -c [program]

在上一节中,你看到了谁正在使用/usr/sbin/sshd。虽然得到了一些结果,但这还不是全部。任何特定的程序实际上都是由几个(或很多)对其他进程、程序、套接字和设备的调用组成的,所有这些对于Linux来说都不过是更多的文件而已。为了找出与正在运行中的特定程序关联的所有其他文件,可以在lsof命令后面加上-c选项,然后是正在运行的(也就是“打开”的)程序的名称。例如,lsof /usr/sbin/sshd会报告与这个确切的文件关联的两个用户和三个打开的文件。但对于完整的sshd命令,又会怎样呢?

# lsof -c sshd
COMMAND  PID  USER  NAME
sshd   10542  root  /lib/ld-2.3.5.so
sshd   10542  root  /dev/null
sshd   10542  root  192.168.0.170:ssh-
>192.168.0.100:4577
➥(ESTABLISHED)
sshd   10548  scott  /usr/sbin/sshdp
sshd   10548  scott  192.168.0.170:ssh-
>192.168.0.100:4577
➥(ESTABLISHED)

从前面的代码中可以看到,这94行(代表94个打开的文件)中的某些行与sshd程序有某种关联。一个是.so文件(共享对象,类似于Windows中的DLL文件),其余文件中的部分文件表明这台计算机和其他计算机之间的网络连接(实际上,这个网络上的其他计算机是通过SSH连接到这台计算机的。有关这个进程的更多内容,参见15.1节)。

对计算机上的各种命令应用lsof,可以找到大量信息,至少可以知道现代的程序是多么复杂。在每天使用的软件上试试这个命令,你可能会对提供软件的辛勤工作的开发人员萌生新的敬仰之情。

说明 lsof命令的选项数量多得让人吃惊,这里看到的只是它很小的一个子集。lsof的源代码包含一个名为00QUICKSTART的文件(前面两个是0),它是一个教程,介绍了关于这个命令的一些更为强大的特性。在Google中搜索一下这个文件名,开始阅读吧。

free

现在大多数计算机都有数百MB(megabyte),甚至几GB(gigabyte)的RAM,但仍然可能因为大量的内存使用或虚拟内存交换,而使计算机的运行速度慢下来。要查看系统内存的当前状态,可以使用free命令。

$ free 
        total    used   free  shared buffers cached
Mem:  1036136  995852  40284       0   80816 332264
-/+ buffers/cache:    582772  453364
Swap: 1012084  495584 516500

在默认情况下,free命令以千字节为单位显示结果,与使用-k选项一样。不过,可以改变这一设置。-b选项以字节为单位显示内存使用大小,而-m选项(可能是最常用的选项)则以兆字节为单位。

$ free -m
                  total used free shared buffers
cached
Mem:         1011   963   48    0     78     316
-/+ buffers/cache:  569  442
Swap:         988   483  504

free命令的输出可以看出,这台计算机有1011 MB可用的物理RAM(实际是1024 MB,但free命令显示为1011 MB,因为内核占用了剩下的13 MB,所以其他用户永远也不能使用这部分内存)。第一行显示有963 MB RAM正在使用中,只剩下48 MB空闲的RAM。交换区(或虚拟内存)差不多有1 GB,已经使用了大约一半(或483 MB)。表面看起来就是这样的。

不过,事情并没有那么简单。重要的是中间标记为-/+ buffers/ cache的那一行。硬盘驱动器使用缓冲(buffer)和缓存(cache)来加速访问,如果一个程序需要内存,系统就能够快速地为该程序分配内存。对于Linux计算机中正在运行的应用程序而言,现在可以使用的空闲内存有442 MB,缓存空间为569 MB(如果需要的话,可以使用)。除了交换空间,它们也是可以使用的空间。如果Linux的内存管理做得没有那么高效的话,那么它也就什么也不是了。

提示 有关free命令和Linux内存管理的更多信息,可以在Gentoo Wiki上找到,网址为http://gentoo-wiki.com/FAQ_Linux_Memory_Management。虽然据称这篇文章针对的是Gentoo(一个独特的Linux分发版),但它仍然适用于所有版本的Linux系统。

df

free命令处理的是系统的RAM,而df命令(可以认为是disk free的缩写)处理的则是系统中硬盘空间的大小。运行df,就会列出系统中可以使用的磁盘空间,每个空间已经使用了多少,及它们各自挂载到文件系统的什么位置。

$ df
Filesystem  1Kblocks    Used  Available Use% Mounted on
/dev/hda1    7678736  5170204   2508532  68% /
tmpfs         518068        0    518068   0% /dev/shm
tmpfs         518068    12588    505480   3% 
➥/lib/modules/2.6.12-10-386/volatile
/dev/hda2   30369948 24792784   5577164  82% /home

在更详细地查看这些结果之前,先让它们变得更易于阅读些。默认情况下,df命令是按以千字节(KB)为单位来显示结果的,但如果这里换用-h(或--human-readable)选项,结果通常会更容易理解。

$ df -h
Filesystem  Size  Used  Avail Use% Mounted on
/dev/hda1   7.4G  5.0G  2.4G  68% /
tmpfs       506M     0  506M   0% /dev/shm
tmpfs       506M   13M  494M   3% /lib/modules/
➥2.6.12-10-386/volatile
/dev/hda2    29G   24G  5.4G  82% /home

“人类可读的(human-readable)”(顺便说一下,这个词非常好)意味着千字节将标识为K、兆字节为M、吉字节则为G。在上面的列表中可以看到最后两个(MG)。

那这些结果都有什么含义呢? 硬盘上有两个分区:/dev/hda1(挂载到/)和/dev/hda2(挂载到/home)。/home分区分配了29 GB的空间,已经使用了其中的82%,还剩5.4 GB的空闲空间。这个分区的磁盘空间还不至于让人恐慌,除了一些有价值的MP3的CD,应该删除一些不需要的文件了。

根(root)分区(或/)的可用空间要少些:只有7.4 GB中的2.4 GB是可用的。不过,这个分区的空间不可能增加了,因为它包含程序和其他相对比较静态的文件。挂载到类似/var路径的分区(在这个例子中是/home),需要在这些分区上安装程序(详情参见第13章),其他一些大小和内容会发生变化的文件(如日志文件)也都位于这些分区。但总地来说,这个分区仍然还有很多空间,尤其是如果不计划安装特别大型的应用程序,也不打算在这台计算机上启动Web或数据库服务器。

其他两个分区都被标识为tmpfs,表示它们是计算机上虚拟内存或交换分区使用的临时文件系统。在关闭计算机后,这些分区的内容就消失了。

提示 有关tmpfs的更多信息,可以看看Wikipedia上的文章,网址为http://en.wikipedia.org/wiki/TMPFS

du

df命令可以提供整个硬盘的情况,但如果只想知道一个目录和它的内容使用了多少空间,又该怎么办呢?du命令(disk usage的缩写)能够解决这个问题。先用cd命令将当前工作目录切换到待查的目录,然后运行du命令。

$ cd music
$ du
36582   ./Donald_Fagen
593985  ./Clash
145962  ./Hank_Mobley/1958_Peckin'_Time
128200  ./Hank_Mobley/1963_No_Room_For_Squares
108445  ./Hank_Mobley/1961_Workout
2662185 .

df命令一样,du命令的结果也默认以KB为单位,也可以像df命令一样,使用-h(或--human-readable)选项,以更易于理解的方式来查看结果。

$ cd music
$ du -h
36M   ./Donald_Fagen
581M  ./Clash
143M  ./Hank_Mobley/1958_Peckin'_Time
126M  ./Hank_Mobley/1963_No_Room_For_Squares
106M  ./Hank_Mobley/1961_Workout
2.6G  .

输出结果显示了每个子目录使用的空间,而对于包含其他目录的子目录,也显示了相应的内容总量。以Hank_Mobley目录为例,它在文件系统中总共占了374 MB。这个总数来源于包含在Hank_Mobley目录中的3个子目录(因为要对以千字节为单位的实际容量进行取整,显示为兆字节,所以总数多少会有点偏差)。如果Hank_Mobley目录占用空间的总数比它的三个子目录空间的总和要大的多的话,那就意味着Hank_Mobley目录包含了很多不包含在任何子目录中的零散文件。

最后,整个music/目录的空间大小是2.6 GB。

du -s

如果不需要一个目录的所有子目录的信息,则可以使用-s选项,让du命令只报告占用空间的总量。

$ cd music
$ du -hs
2.6G      .

简洁明了。

本章介绍的每个命令都有它们各自的GUI。但如果你的系统无法响应,或者你正在通过SSH(第15章将详细介绍该内容)连接到其他计算机而不能使用GUI,又或者你只想使用最快速的工具,那么,都需要使用命令行。本章介绍的命令都非常容易记忆,这要感谢它们精挑细选的名称,如下所示。

  • ps(view running processes,查看正在运行的进程)。

  • kill(kill processes,关闭进程)。

  • top(top listing of running processes,查看正在运行的进程的最新列表)。

  • lsof(list[ls] open files,列出[ls]打开的文件)。

  • free(free, or available, memory,空闲或可用的内存容量)。

  • df(disk free space,磁盘的空闲空间)。

  • du(disk usage,磁盘的使用情况)。

在你的计算机上练习一下这些命令,不要忘记阅读每个命令的man page,因为本章介绍的只是它们强大功能的很小一部分。其中几个命令有很多选项(特别是pstop以及lsof命令)。正因为这样,你才能够在计算机上执行某种真正令人吃惊的侦察工作,将一切都置于你的监视之下。



第13章 安装软件

各种Linux系统默认都会包含很多大型软件包。在完成基本的安装以后,就可以马上在网上冲浪、写报告、创建电子表格、查看和处理图像以及听音乐。即使有了这么多软件,当你偶然遇到什么新工具时,可能还会打算安装它们。幸好,在使用Linux操作系统的计算机中安装新软件十分容易。

有关Linux的一种传统说法是,必须编译要安装的任何软件。如果愿意,当然可以进行编译(而且很多人也是这么做的),但是现在几乎没有必要这么做了。使用一些简单的工具,就可以很容易而且很快地安装好很多软件包。

不过,在继续实践之前,需要先理解一件重要的事情。在Linux世界中,软件包的格式有好多种,尤其以RPM和DEB两种格式为主(不足为奇,本章将要介绍的就是这两种格式)。使用RPM的Linux系统有Red Hat(其实,RPM就代表Red Hat Package Manager)、Fedora Core、SUSE以及其他基于RPM的分发版本。DEB是基于Debian的Linux系统使用的格式,例如Debian、K/Ubuntu、Linspire、Xandros以及其他很多系统都使用这种格式。现在你应该知道这两种格式是怎么回事了,而且应该重点学习与你使用的Linux分发版本相匹配的系统。

提示 各种软件包管理系统,以及使用它们的Linux系统之间存在着的巨大差别,参见DistroWatch的“Linux DistributionsFacts and Figures: What Is Your Distribution's Package Management?”,网址为http://distrowatch.com/stats.php?section=packagemanagement。注意:DEB在各种软件包管理系统中占据领先地位(也要注意,这里之所以有所侧重是因为这本书是就K/Ubuntu编写的,也就是所谓的“Debian做的就是好”)。

rpm -ihv [package]

rpm -Uhv [package]

rpm命令用于安装以.rpm为后缀的软件安装程序,这看起来完全符合逻辑。要安装RPM软件包,需要先下载。用业界标准的开源网络端口扫描器nmap来作为例子。从www.insecure.org/nmap/download.html可以下载nmap的RPM软件包,下载到系统以后,只需运行rpm命令,并提供3个选项:-i(安装软件包)、-h(安装过程中显示hash标记)和-v(显示命令的执行过程)。要运行rpm命令,还必须具有root权限,其基本格式如下所示:

# rpm -ihv nmap-4.01-1.i386.rpm

不过,实际上这还不是你应该运行的命令。一个更好的选择是-Uhv,其中-U代表“update(升级)”。为什么-U-i更好呢?因为选项-i只进行安装,而选项-U则进行升级和安装。如果软件包在系统中已经存在,现在正试图在计算机上安装一个更新的版本,-U选项将执行升级。如果系统中还没有安装过这个软件包,-U选项会注意到这个情况,并安装软件包。所以,只使用-U选项就可以,不管是进行升级还是安装。-U选项可以做好应该做的事,你不必为此担心。

# rpm -Uhv nmap-4.01-1.i386.rpm
Preparing... ############################## [100%]
1:nmap       ############################## [100%]

如果你想一次安装多个RPM,只要把它们依次列在命令后面,并用空格分隔开:

# rpm -Uhv nmap-4.01-1.i386.rpm nmap-frontend-4.01-1.i386.rpm

如果有许多RPM要安装的话,也可以使用通配符。例如,如果在名为software的子目录中有20个.rpm文件,只需要运行以下命令:

# rpm -Uhv software/*.rpm

警告 除了在安装内核时,应该使用-i选项,在绝大多数情况下,-U选项都是更好的选择。如果用-U选项对内核进行升级,新的内核将不能工作。另一方面,如果用-i选项安装了一个新的内核,旧的内核作为备份也仍然在计算机上存在,以防新内核发生崩溃。

rpm -e [package]

删除安装好的RPM比安装它们要更容易。将RPM安装到计算机上用的是-Uhv选项,而删除它们只需要使用-e(代表erase)。如下所示:

# rpm -e nmap

就这么简单。-e选项很安静,没有输出内容。不过,需要注意的是,当使用rpm -Uhv安装软件时,需要指定文件名,否则rpm没办法知道你想安装什么。而当使用rpm -e删除软件时,需要指定的是包名,因为安装文件早就不存在了,rpm是通过包名来识别软件的。

yum install [package]

虽然rpm命令的功能强大,但使用一段时间后,当试图安装需要依赖其他东西的软件包时,就会很快遇到问题,也就是所谓的“依赖关系地狱(dependency hell)”问题。为了安装软件包A,还需要下载和安装软件包B和C,但为了安装C,还需要下载和安装软件包D和E,但为了安装E……唉,没完没了!

早在几年前,基于Debian的Linux系统(本章稍后会介绍这个系统)就用功能强大而有用的apt解决了这个问题。基于RPM的Linux系统也能够使用apt,但经常使用的是相对来说比较新但还不是很成熟的yumyum起初是为基于RPM的Yellow Dog这一发行版的Linux系统开发的(因此这也是yum这个名称的来源,它是“Yellow Dog Updater, Modified”的简称),现在它已经得到广泛地使用,但在功能和可用性上仍然落后于apt。不过,因为很多系统附带了yum,所以本书将介绍一下。

yum命令是rpm的一个包装器,可以安装、升级和卸载软件包。除此以外,它还可以自动处理软件包之间的依赖性。例如,如果想安装前面例子中的软件包A,yum就可以自动下载和安装A、B、C等软件包。以后,如果想在系统中删除A,yum也可以卸载它,如果系统中没有其他软件包需要B和C,yum也一并卸载它们。

yum安装软件也相当容易。假设你想安装一个媒体播放器XMMS(不足为奇,这个名字代表的是X Multimedia System)。为了安装XMMS,还需要再安装几个它依赖的其他软件。有了yum,这一安装过程就不像手工用rpm逐一安装它们时那么麻烦了。首先,不需要亲自查找和下载xmms软件包,因为yum会为你自动下载好XMMS和其他所有的依赖包。

美中不足的是,yum在进行处理时,它的输出内容极为啰嗦。以下列举的输出已经删减了大量内容,但还是显得有些冗长。无论如何,这些内容应该可以帮你了解使用yum时,能够看到的大致内容。

# yum install xmms
Setting up Install Process
Setting up repositories
update 100% |======================|  951 B   00:00
base   100% |======================| 1.1 kB   00:00


Resolving Dependencies
--> Populating transaction set with selected
➥packages. Please wait.
---> Downloading header for xmms to pack into transaction set.
xmms-1.2.10-9.i386.rpm 100% |==========| 24 kB 00:00
--> Restarting Dependency Resolution with new changes.

---> Downloading header for gtk+ to pack into transaction set.
gtk%2B-1.2.10-33.i386.rpm 100% |==========| 23 kB 00:00

Dependencies Resolved 

 Package    Arch Version      Repository       Size

Installing:
 xmms       i386 1:1.2.10-9   base             1.9 M
Installing for dependencies:

 libogg     i386 2:1.1.2-1    base             16 k
 libvorbis  i386 1:1.1.0-1    base             185 k



Total download size: 3.3 M
Is this ok [y/N]:

输入y以后,yum就会下载和安装软件包,并继续报告它正在进行的每一步操作。

Downloading Packages:
...
(5/6): libogg-1.1.2-1.i38 100% |==========| 16 kB   00:00
(6/6): libvorbis-1.1.0-1. 100% |==========| 185 kB  00:00
Running Transaction Test

Running Transaction 
  Installing: libogg      ########## [1/6]
  Installing: libvorbis   ########## [2/6]

...
Installed: xmms.i386 1:1.2.10-9
Dependency Installed: gdk-pixbuf.i386
➥1:0.22.0-17.el4.3 gtk+.i386 1:1.2.10-33 libogg.i386
➥2:1.1.2-1 libvorbis.i386 1:1.1.0-1 mikmod.i386 0:3.1.6-32.EL4

哎呦,总算完了! XMMS终于安装完成,可以使用了。接下来,再看看当你不喜欢XMMS时,如何删除它。

yum remove [package]

yum有个值得称道之处: 它的命令语法对用户非常友好。要安装软件包?就用yum install。要删除安装的软件?就用yum remove。如果你厌烦了XMMS,只要运行这个命令,如下所示:

# yum remove xmms
Setting up Remove Process
Resolving Dependencies

---> Package xmms.i386 1:1.2.10-9 set to be erased

Dependencies Resolved 

 Package  Arch  Version     Repository   Size

Removing:
 xmms     i386  1:1.2.10-9  installed    5.2 M

Is this ok [y/N]:

即便是像删除软件包这么简单的事情,yum也坚持它的一贯作风,抓住你,详细告诉你它正在做什么。按y键确认卸载,yum将显示一小段信息,如下所示:

Running Transaction Test

Running Transaction 
  Removing  : xmms     #################### [1/1]
Removed: xmms.i386 1:1.2.10-9
Complete!

现在计算机上已经没有XMMS了。注意,yum为XMMS安装的软件包(详情参见13.3节)并没有随xmms包一起删除。XMMS的运行需要这些依赖的软件包,而且它们也要为计算机上的其他不同程序服务,所以yum允许它们继续保留在系统中(apt的默认行为也是这样的,有关信息参见13.9节)。

yum update

Linux系统中会包含很多软件包,就算没有几千个,至少也有几百个,这些软件包经常需要升级。手工查找安装过的软件的每个新版本,再安装更新,将会是件很费时间的工作。幸好,yum可以让这一处理变得非常容易。一个简单的yum update命令就能让yum为它正在跟踪的软件检查是否有任何更新。如果有新的软件包可以用,yum就会显示有哪些可用的内容,并请求你的允许,以便决定是否继续安装更新。

# yum update
Setting up Update Process
Setting up repositories
update 100% |====================| 951 B     00:00
base   100% |====================| 1.1 kB    00:00


Resolving Dependencies
--> Populating transaction set with selected
➥packages. Please wait.
---> Downloading header for cups-libs to pack into 
➥transaction set.
cups-libs-1.1.22-0.rc1.9. 100% |==========| 22 kB   00:00
---> Package cups-libs.i386 1:1.1.22-0.rc1.9.10 set
➥to be updated

--> Running transaction check
Dependencies Resolved
Package  Arch  Version     Repository Size

Installing:
openssl  i686 0.9.7a-43.4       update 1.1 M
pam      i386 0.77-66.13        update 1.8 M
perl     i386 3:5.8.5-24.RHEL4  update 11 M
udev     i386 039-10.10.EL4.3   update 830 k
wget     i386 1.10.2-0.40E      update 567 k
Transaction Summary

Install       1 Package(s)
Update       11 Package(s)
Remove        0 Package(s)
Total download size: 30 M
Is this ok [y/N]:

如果这时按y键,就表示同意yum下载并安装这12个软件包。在输出很多操作过程后,yum完成了任务,计算机中的软件现在就是最新版本的了。想冒险生活在最新技术的边缘吗(bleeding edge)①?那就每天运行yum update吧。如果你并不渴望一直使用最新最好的软件版本,可以间隔较长的一段时间再运行yum update,但无论如何都应该确保定期运行一次这个命令。软件总会不断推出各种安全更新,所以让软件版本保持最新,是个不错的主意。

① 在计算机领域,Bleeding Edge指一种最新的、因而也并非完美的技术。使用者为了它的新,就要拿稳定性和产量来冒险。它也指当今技术的每一步发展都越来越昂贵的趋势。这个词的发明者是Peter Barus,他是一位Superbase程序员。——译者注

yum search [string]

yum list available

现在,你已经知道了如何用yum安装和删除软件,但是如何最先找到需要的软件呢?假设你现在对GIMP(GNU Image Manipulation Program)感兴趣,想知道是否有与GIMP有关的软件包供yum安装。可以使用yum search gimp命令,但这并不是个好办法。对于提供的搜索项,这个命令会在所有软件包的名称、描述、摘要甚至在打包者(packager)的名称列表中搜索可以匹配的内容。最终得到的结果列表可能会像Bill Gates的银行对账单那么长。

一种更好的办法就是先通过yum来查询可用的软件包(这通常会生成另一个疯狂增长的列表),接着再将结果通过管道发送给grep,让它继续搜索。

$ yum list available | grep gimp
gimp.i386             1:2.0.5-5    base
gimp-devel.i386       1:2.0.5-5    base
gimp-help.noarch      2-0.1.0.3    base
gimp-print.i386       4.2.7-2      base

这样目前就只显示11个可使用的结果。如果想执行完整的搜索,可以使用yum search;否则,使用list availablegrep就够用了。大多数情况下,后面这种方法才是你真正想要的,而且也能更容易地找到能用的东西。

dpkg -i [package]

安装新软件应该是在Linux计算机上可以做的最有趣的事情。在接下来的几节中将会看到,在Debian中有apt,在任何Linux发行版本中,这是一种功能最强大,也最容易使用的安装软件的方法。虽然apt很强大,但很大程度上它是dpkg程序的包装器(与yumrpm的包装器一样),由这个程序完成在基于Debian的计算机上安装和删除软件的苦差事。在学习如何使用apt之前,应该先学习如何使用dpkg,因为并不是任何东西都能用apt来安装。

举个比较合适的例子:现在最流行的一个网络电话(Voice over IP,VoIP)程序是Skype。不过,因为使用授权问题,大多数Linux发行版安装中都没有默认包含Skype。如果你想使用Skype,就必须先从网站下载这个程序,再手工安装它。要得到Skype,先去它的Linux下载页(位于www.skype.com/products/skype/linux),再找到适合你的Linux系统的软件包。在这个例子中,应该使用为Debian提供的软件包,编写本书时这个软件包的名称是skype_1.2.0.18-1_i386.deb

在把相应的.deb文件下载到你的系统中以后,就可以安装它了。首先使用cd命令将工作目录切换到包含.deb文件的目录,再使用dpkg安装它。

说明 在大多数基于Debian的Linux系统中,运行这个及其他所有的dpkg命令都需要具有root权限。然而,广受欢迎的K/Ubuntu发行版可以不使用root,通常需要root权限才能运行的命令用sudo也可以正常运行。换句话说,在Debian中使用命令如下所示:

  # dpkg -i skype_1.2.0.18-1_i386.deb

在K/Ubuntu和其他支持sudo的发行版中应该按以下方式使用这个命令:

  $ sudo dpkg -i skype_1.2.0.18-1_i386.deb

本书的例子是在运行K/Ubuntu的计算机上编写的,所以你看到的是sudo而不是root,现在应该明白是怎么回事了吧。

# ls
skype_1.2.0.18-1_i386.deb
# dkpg -i skype_1.2.0.18-1_i386.deb
sudo dpkg -i skype_1.2.0.18-1_i386.deb
Selecting previously deselected package skype.
(Reading database ... 97963 files and directories
➥currently installed.)
Unpacking skype (from skype_1.2.0.18-1_i386.deb) ...
Setting up skype (1.2.0.18-1) ...

这就是dpkgdpkg命令是简练的典范,只告诉你重要的信息,不多说其他的。

dpkg -r [package]

-i选项代表install(安装),用于在基于Debian的计算机中安装软件;类似地,-r选项代表remove(删除),用于在基于Debian的计算机中卸载软件。如果你厌倦了Skype,想把它从计算机中删除,这也是小事一桩。

# dpkg -r skype
(Reading database ... 98004 files and directories
➥currently installed.)
Removing skype ...

当使用dpkg -i安装软件时,需要指定文件名,否则dpkg没有办法知道你想安装什么。而当使用dpkg -r删除软件时,需要指定的是包名,因为安装文件早就不存在了,apt是通过包名来识别软件的。

apt-get install [package]

虽然dkpg命令的功能强大,但使用一段时间后,当你试图安装需要依赖其他东西的软件包时,就会很快遇到问题(也就是所谓的“依赖关系地狱(dependency hell)”问题)。为了安装软件包A,还需要下载和安装软件包B和C,但为了安装C,还需要下载和安装软件包D和E,但为了安装E……唉,没完没了!这时就需要apt出手了!

apt命令和它的各个选项可以用于安装、升级和卸载软件包。但apt的最大优点是可以自动处理软件包之间的依赖关系。例如,如果你想安装前面例子中的软件包A,apt就可以自动下载和安装A、B、C等软件包。以后,如果想在系统中删除A,apt也可以卸载它,如果系统中没有其他软件包需要B和C,apt也一并卸载它们。

apt最初是为在Debian系统中作为dpkg的前端使用而开发的。现在在每个基于Debian的Linux系统(例如Debian、K/Ubuntu、Linspire、Xandros以及其他很多Linux系统)中都可以看到apt的身影,而且它是可以让Debian易于使用且功能强大的特性之一。此外,非Debian的Linux系统也意识到了apt是多么优秀,最终Connectiva公司对apt进行了改造,使它能够管理RPM(本章重点介绍在Debian中如何使用apt)。

提示 在基于RPM的Linux发行版本中使用apt的不错的简单介绍,可以参考本书作者的一篇文章“A Very Apropos apt”(2003年10月发行的Linux Magazine杂志中有这篇文章,也可以访问www.linux-mag.com/2003-10/apt_01.html)。除了这篇文章,也应该看看http://apt.freshrpms.net 上最新的RPM库。注意,要访问Linux Magazine的内容,得先进行免费的注册。

假设现在想用apt来安装非常棒的工具sshfs。为此,需要经历以下处理过程(记住必须以root用户来运行这些命令):

# apt-get update
Get:1 http://us.archive.ubuntu.com breezy Release.gpg [189B]
Get:2 http://archive.ubuntu.com breezy Release.gpg [189B]


Hit ftp://ftp.free.fr breezy/free Sources
Hit ftp://ftp.free.fr breezy/non-free Sources
Fetched 140kB in 1m4s (2176B/s)
Reading package lists... Done
[Results truncated for length]
# apt-get install sshfs
Reading package lists... Done
Building dependency tree... Done
The following extra packages will be installed:
  fuse-utils libfuse2 
The following NEW packages will be installed:
  fuse-utils libfuse2 sshfs

Need to get 96.9kB of archives.
After unpacking 344kB of additional disk space will be used.
Do you want to continue [Y/n]? y
Get:1 http://us.archive.ubuntu.com breezy/universe
➥sshfs 1.1-1 [19.3kB]
...
Fetched 96.9kB in 10s (9615B/s)
Reading package fields... Done
Reading package status... Done
Preconfiguring packages ...
...
Selecting previously deselected package sshfs.
Unpacking sshfs (from .../archives/sshfs_1.1-1_i386.deb) ...

Setting up sshfs (1.1-1) ...

实际上,刚才运行了两个命令,我们来看看它们是做什么的。apt-get update命令会从apt服务器[也称为仓库(repository)]下载当前可用的软件包列表,在apt配置文件(/etc/apt/sources.list)中可以设置apt服务器(如需你想看看apt仓库在哪,只需运行cat /etc/apt/sources.list)。如果在运行apt-get update命令以后,在显示信息的第一行看到的是Get,就意味着apt发现远程仓库中的软件列表比本地的更新,所以它就下载这个更新的列表。反之,如果看到的是Ign,就说明远程仓库上的软件列表和你的计算机上的列表是同步的,不需要下载。在做其他事之前先运行apt-get update,就可以确保计算机上的软件包列表是正确的,而且也是最新的。

apt-get install sshfs命令用于取回指定的软件包,以及任何必需的依赖(在这个例子中需要依赖fuse-utilslibfuse2)。在把这些软件包下载到本机以后,apt(其实是dpkg根据apt的指示来进行操作)就会安装好所有软件包。请时刻记住这些命令使用的是包名,而不是文件名。换句话说,应该使用apt-get install sshfs,而不是apt-get install sshfs_1.1-1_i386.deb。可以看到,如果apt发现了正在安装的软件包所要依赖的其他软件包(就像安装sshfs时的情况那样),在apt取回这些依赖包之前,它会要求确认是否确实需要安装它们。

如果想一次安装多个软件包,只需将它们依次都列在命令行上。例如,如果你想安装sshfsshfs-utils,可以用以下命令:

# apt-get install sshfs shfs-utils

只要找到了sshfsshfs-utils的任何依赖包,apt就会询问是否需要安装它们。嗯,apt就这么简单。

提示 还不知道sshfs是什么吗?哦,你应该知道它的!看看本书作者在The Open Source Weblog上写的一篇日志“Mount Remote Drives via SSH with SSHFS”(http://opensource.weblogsinc.com/2005/11/03/mount-remote-drives-via-ssh-with-sshfs/)。

apt-get remove [package]

如果系统不再需要某个软件包了,用apt也可以轻易地卸载它: 这次用的不是apt-get install,而是apt-get remove。它的功能与apt-get install正好相反:卸载指定的软件包,以及相关的任何依赖程序。注意,这个命令要引用的是包名,而不是文件名,所以得运行apt-get remove sshfs,而不是apt-get remove sshfs_1.1-1_i386. deb

# apt-get remove sshfs
Password:

The following packages will be REMOVED:
  sshfs 


After unpacking 98.3kB disk space will be freed.
Do you want to continue [Y/n]?

不过,卸载软件包时并不会删除这个包的所有东西,被删除软件的配置文件还会残留在计算机中。如果你确实想要删除所有东西,则应该加上--purge选项,如下所示:

# apt-get --purge remove sshfs
Password:

The following packages will be REMOVED:
  sshfs* 
After unpacking 98.3kB disk space will be freed.
Do you want to continue [Y/n]?

使用--purge选项后,在apt将要删除的包名后面将用星号(*)加以标识,表示与这个包相关的配置文件也要被删除。

apt-get upgrade

现在的Linux系统上总会有几千个软件包在运行,每天肯定至少有一个软件包需要更新。有了apt,保持你的系统为最新状态就很容易了。处理过程如下所示(记住,这个命令也必须以root来运行):

# apt-get update
Get:1 http://us.archive.ubuntu.com breezy Release.gpg [189B]
Get:2 http://archive.ubuntu.com breezy Release.gpg [189B]

Hit ftp://ftp.free.fr breezy/free Sources
Hit ftp://ftp.free.fr breezy/non-free Sources
Fetched 140kB in 1m4s (2176B/s)
Reading package lists... Done
[Results truncated for length]
# apt-get upgrade
Reading package lists... Done
Building dependency tree... Done
The following packages have been kept back: 
  koffice
The following packages will be upgraded:
  kalzium kamera kanagram karbon kbruch kchart
➥kcoloredit kdegraphics kdegraphics-kfile-plugins
...
53 upgraded, 0 newly installed, 0 to remove and 1 not upgraded.
Need to get 58.3MB of archives.
After unpacking 28.7kB of additional disk space will be used.
Do you want to continue [Y/n]?

我们来看看这里发生了什么。先运行apt-get update命令,以便让你的计算机和apt仓库保持同步。接着apt-get upgrade检查本机已经安装过的软件包与apt仓库可用的软件包是否有任何区别。如果存在区别,apt就会显示它将要下载和安装的软件包列表。当然,实际的软件包列表会根据系统的更新程度而有所变化。在这个例子中,要更新53个软件包,得花不少时间。

如果输入yapt就会把这53个软件包下载到/var/cache/apt/ archives。全部下载到计算机上以后,apt再安装它们。如果你不想进行升级,只需要输入n

这样的命令已经够容易了,而使用apt升级Linux系统最有效的方法是把这些命令连接起来使用:

# apt-get update && apt-get upgrade

&&可以确保只有apt-get update成功完成以后,才会运行apt-get upgrade。当然,更好些的办法是在.bash_aliases文件中为这个命令创建一个别名,操作方法可以参见11.8节。

alias upgrade='apt-get update && apt-get upgrade '

重新加载.bash_aliases这个文件,现在只需要输入upgrade,按Enter键,再按Y键以接受任何新的软件包,就完成了所有升级。Windows升级,伤心去吧!

apt-cache search

前面我们介绍了很多用apt安装软件的方法,但是如何最先找到需要的软件呢?在apt工具箱还有另一个工具可以帮上你的忙: apt-cache search,它可以搜索apt仓库中可用的软件包列表。这里有个不错的变化,使用apt-cache search不需要具有root权限。

$ apt-cache search dvdcss
libdvdread3 - Simple foundation for reading DVDs
ogle - DVD player with support for DVD menus
libdvdcss2 - portable library for DVD decryption
libdvdcss2-dev - development files for libdvdcss2

有关如何使用这个命令进行搜索需要记住几件事。它对你输入的字符串(在这个例子中是dvdcss)进行模糊匹配,而不是精确匹配整个词。其次,与搜索模式匹配的内容可能位于包的名称或描述。最后,apt-cache search是对整个软件包列表(包括安装的和尚未安装的软件包)进行搜索,所以你可能已经安装了搜索结果中显示的某些软件包。

提示 Synaptic是apt的一个GUI,它几乎可以完成这里介绍的所有功能,而且操作方式是通过鼠标点击而不是键盘输入。尤其是它的搜索工具做得非常漂亮,所以通常只用Synaptic进行搜索,而其他的所有事则通过命令行来完成。这样似乎可以最好地发挥每种工具的效率。

apt-get clean

在使用apt下载和安装好软件包以后,.deb文件将仍然保留在/var/cache/apt/archives/目录中。时间一长,这些不必要的安装文件可能就会占据大量的磁盘空间。删除这些不需要的.deb文件,可以使用apt-get clean命令(注意,运行这个命令需要具有root权限),如下所示:

$ ls -1 /var/cache/apt/archives/
fuse-utils_2.3.0-1ubuntu1.1_i386.deb
libfuse2_2.3.0-1ubuntu1.1_i386.deb
lock
partial/
sshfs_1.1-1_i386.deb
# apt-get clean
$ ls -1 /var/cache/apt/archives/
lock
partial/

如果因为某些原因,下载中断了,那么可能可以在/var/cache/ apt/archives/partial/目录中找到部分.deb文件。如果你确信所有更新和升级都已经完成和安装好了,那么删除这个目录(所有东西都在这个目录中)的内容应该是安全的。

当然,即使apt很强大,但在使用过程中仍然可能会遇到问题。下面列举一些常见问题及处理方法。

可能给你来个当头一棒的简单问题是“不能打开锁定的文件(Could not open lock file)”这样的错误信息。如果你运行apt-get命令,却没有成功,而且出现了下面这样的错误信息:

E: Could not open lock file /var/lib/dpkg/lock -
➥open (13 Permission denied)
E: Unable to lock the administration directory
➥ (/var/lib/dpkg/), are you root?

解决这个问题的正确方法就在第二行: 你没有登录为root! 只要以root登录,再运行一次,应该就没有问题了。

说明 如果你正在用K/Ubuntu,或者其他任何能够用sudo来代替root的Linux系统,这个错误消息意味着你没有在命令前面加上sudo。换句话说,如果按以下这样运行命令:

  $ apt-get upgrade

就会出现那样的错误消息,应该按以下方式来运行这个命令:

  $ sudo apt-get upgrade

apt抱怨软件包有不能满足的依赖关系时,通常会出现另一个常见问题。例如,apt建议运行apt-get -f install命令时,这个问题就正在发生了。程序给出的这一建议是在告诉你:系统中有无法满足的依赖关系,apt因此无法完成任务。

这个问题有两种可能的解决方法。可以按照apt的建议,运行apt-get -f install,下载和安装必需的软件包来修复这一问题。这个方法通常能够解决问题,并继续进行安装。

如果你不想这么做,也可以试着运行一下apt-get -f remove,删除apt认为有问题的软件包并尝试修复问题。这些方法看起来似乎都具有一定的潜在危险(如果稍不留神,可能真就这样了),但每种方法都会给你机会先看看将要发生什么变化,并征得你的同意。在同意命令继续执行之前,务必要检查apt提供的建议。

最后,apt可能会警告你一些软件包会“被阻止更新(have been kept back)”。这个警告是说,apt已经发现了请求安装的包(或它的依赖包)和系统中已经安装的另一个包之间存在冲突。要解决这个问题,可以试试用-u选项来安装被阻止的软件包,这样在安装时可以精确地显示出需要升级什么软件包。

虽然基于RPM和Debian的Linux系统之间存在不少区别,但在软件包管理方面有一些相似之处,它们都针对简化软件的安装、删除和管理进行了特殊的设计。基于RPM的Linux系统使用rpm命令来安装、升级和删除软件,而基于Debian的Linux系统则使用dpkg命令来完成相同的操作。为了解决令人头疼的“依赖关系地狱”问题,基于RPM的Linux系统使用的方法是yumrpm的一个包装器),而基于Debian的Linux系统使用的则是aptdpkg的一个包装器)。这两种技术也存在差别,主要体现在它们的成熟度和易用性上。在这些方面,apt无疑要领先于yum,但yum也在不断改进。

最后,这两种技术仍然要胜过Windows用户不得不忍受的软件更新升级机制。Windows Update只更新Microsoft自己的软件和为数不多的第三方硬件驱动程序,而aptyum基本上可以处理Linux系统中的每一种软件。与Microsoft用户相比,这就是Linux用户拥有的一个巨大优势,它应该值得我们引以为豪。



第五部分 网络

第14章 连接

第15章 使用网络

第16章 Windows联网



第14章 连接

当Linux操作系统最初还只是一小群程序员拼装起来的简陋内核时,网络已经就是Linux系统的一部分了。在成就Linux的众多因素中,网络是当之无愧的核心因素。Linux 网络大部分时间都能很好的工作,为你提供稳定可靠的网络连接,而且可以不断地加以优化和调整,直到满足你的需要。

本章介绍如何测试、度量以及管理网络设备和连接。当系统运行正常时(绝大部分时间都是这样的),可以使用本章介绍的工具来监视系统的网络连接。在遇到问题时,本章介绍的命令也能够帮助你解决一些棘手的问题。

提示 本章假设你使用的是IPv4寻址协议,其格式为xxx.xxx.xxx.xxx。IPv6最终将取代IPv4,但这是比较遥远的未来。到那个时候,现在使用的route和其他许多命令都会发生变化。眼下,本章介绍的内容正是现在需要的东西。有关IPv6的更多信息,可以参考Wikipedia的“IPv6”(http://en.wikipedia.org/wiki/Ipv6)。

ifconfig

本章介绍的所有内容都要依靠网络连接。在14.13节中,会介绍一些如何解决网络连接问题的方法。不过,这里先从最基本的开始,查找一下现在你有什么网络连接,以及它们的状态。

要快速查看系统的网络设备,以及它们是否正在运行,可以使用ifconfig(代表interface configuration,接口配置)命令和它的-a(代表all)选项。在一台笔记本电脑上运行这个命令,可能看到以下信息(注意,一些Linux分发版要求以root用户登录,才能使用ifconfig):

$ ifconfig -a
ath0 Link encap:Ethernet  HWaddr 00:14:6C:06:6B:FD 
 inet addr:192.168.0.101  Bcast:192.168.0.255 Mask:255.255.255.0
 inet6 addr: fe80::214:6cff:fe06:6bfd/64 Scope:Link 
 UP BROADCAST RUNNING MULTICAST  MTU:1500 Metric:1
 RX packets:1257 errors:7557 dropped:0 overruns:0 frame:7557 
 TX packets:549 errors:2 dropped:0 overruns:0 carrier:0
 collisions:0 txqueuelen:200
 RX bytes:195869 (191.2 KiB) TX bytes:95727 (93.4 KiB)
 Interrupt:11 Memory:f8da0000-f8db0000

eth0 Link encap:Ethernet  HWaddr 00:02:8A:36:48:8A
 BROADCAST MULTICAST  MTU:1500  Metric:1 
 RX packets:0 errors:0 dropped:0 overruns:0 frame:0
 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
 collisions:0 txqueuelen:1000
 RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)

lo   Link encap:Local Loopback
 inet addr:127.0.0.1  Mask:255.0.0.0
 inet6 addr: ::1/128 Scope:Host
 UP LOOPBACK RUNNING MTU:16436 Metric:1
 RX packets:11092 errors:0 dropped:0 overruns:0 frame:0
 TX packets:11092 errors:0 dropped:0 overruns:0 carrier:0
 collisions:0 txqueuelen:0
 RX bytes:982629 (959.5 KiB)  TX bytes:982629 (959.5 KiB)

这里列出了3个网络接口:ath0(一个无线网卡)、eth0(一个以太网卡)和lo[环回(loopback)接口,详情稍后介绍]。对于每一个网络接口,显示的信息有可连接类型、MAC(Media Access Control)或硬件地址、IP地址、广播地址和子网掩码,还有接收和发送的数据包的信息,以及其他信息。如果断开某个连接,相应的许多信息就会消失。其实,用这种方法可以看到,ath0lo的网络连接是打开的,而eth0则断开了:eth0没有IP地址,也没有其他重要信息。当然,一种更简单的判断方法是:ath0lo接口信息的第4行是以UP开始的,而eth0则没有这个信息。

以相反的顺序介绍网络接口,先来介绍第三个网络接口。lo是环回地址,用于访问机器本身。这个环回地址总是由IP地址 127.0.0.1来代表。基本上,系统需要它才能正常工作。如果能够看到这个接口,你可能不会为它操什么心。但如果看不到这个接口,你才会去了解这个接口,因为你的系统有麻烦了。

提示 有关环回接口和地址的更多信息,可以参考Wikipedia的“Loopback”,网址为http://en.wikipedia.org/wiki/Loopback

eth0是一个以太网卡,就是插入网线的那个网卡。因为当前没有把网线插到这个网卡上,所以该网卡不是活动的,也没有任何地址:IP、广播地址以及子网掩码。一台计算机上可能同时具有有线和无线网络接口,不过通常没有这个必要。

最后是ath0,这是一个无线PCMCIA卡。如果某个无线网卡是主要的网络接口,可能就会看到一个名字与eth0类似的无线网卡接口。如果该无线网卡是次要的,可能看到的就类似于eth1。在插入无线网卡后,K/Ubuntu系统会自动识别该网卡,并配置系统,以启用这个新网卡,将其标识符设置为ath0。因为无线网络接口其实就是带有一些额外的无线向导的以太网接口,所以通过ifconfig得到的有关ath0的信息看起来与eth0的类似(如果启用这个网卡)。

说明 网络设备也可能使用其他的名字,例如,无线网卡的名字也可能是wlan0

ifconfig –a命令会显示全部的接口,包括那些没有启用的接口;而单用ifconfig命令时,只显示启用的网络连接。用这种方法可以快速地检查网络接口的状态,尤其是可以快速找到当前的IP地址。

说明 也可以用ifconfig来配置网络接口,14.5节将介绍这一操作过程。

ping

ping –c

ping命令能够向指定的IP地址发送一种特殊的数据包(ICMP ECHO_REQUEST消息)。如果那个地址上的计算机正在监听ICMP消息,它将用ICMP ECHO_REPLY数据包做出响应(确实,防火墙可以阻止ICMP消息,使ping无效,但大多数时候这并不是问题)。如果ping响应成功,则意味着两台计算机之间的网络是连通的。

$ ping www.google.com
ping www.google.com
PING www.l.google.com (72.14.203.99) 56(84) bytes of data.
64 bytes from 72.14.203.99: icmp_seq=1 ttl=245 time=17.1 ms
64 bytes from 72.14.203.99: icmp_seq=2 ttl=245 time=18.1 ms

[Results truncated for length]--- www.l.google.com
ping statistics ---
6 packets transmitted, 5 received, 16% packet loss, time 5051ms
rtt min/avg/max/mdev = 16.939/17.560/18.136/0.460 ms

直到按下Ctrl+C组合键,ping命令才会停下来。如果忘记了还在使用ping命令,这可能会导致问题,因为它将一直运行下去,直到它被停止或计算机的网络连接中止。我曾经有一次忘记关闭ping,让它连续运行了18天,向我的一个服务器发送了近1.4百万条ping消息。哎,真是糟糕啊!如果想给ping加一些限制,仅发送一定数量的数据包,则可以使用-c选项,后面跟上一个数字。在ping发送的数据包个数达到指定的数量后,就会停止,并报告结果。

$ ping -c 3 www.granneman.com
PING granneman.com (216.23.180.5) 56(84) bytes of data.
64 bytes from 216.23.180.5: icmp_seq=1 ttl=44 time=65.4 ms
64 bytes from 216.23.180.5: icmp_seq=2 ttl=44 time=64.5 ms
64 bytes from 216.23.180.5: icmp_seq=3 ttl=44 time=65.7 ms
--- granneman.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss,
time 4006ms
rtt min/avg/max/mdev = 64.515/65.248/65.700/0.510 ms

对于判断基本的网络连通性,ping命令是一种标准而又快速的方法。而且使用了–c选项,效果会更好,这样就再也不会忘记关闭它,让ping意外地连续运行18天了。

有关使用ping来诊断网络连接故障的更多信息,可以参考“网络问题疑难排解”一节。

traceroute

traceroute命令能够显示数据包从你的计算机路由到指定的主机上时经过的每一步。假设你想知道为什么无法访问www.granneman.com,昨天还能很好地访问它,但今天加载这个网页就超时了。哪出了问题?

$ traceroute www.granneman.com
traceroute to granneman.com (216.23.180.5), 30 hops
➥max, 38 byte packets 
 1  192.168.0.1 (192.168.0.1)  1.245 ms  0.827 ms  0.839 ms
 2  10.29.64.1 (10.29.64.1)  8.582 ms  19.930 ms  7.083 ms
 3  24.217.2.165 (24.217.2.165)  10.152  ms 25.476  ms 36.617 ms
 4  12.124.129.97 (12.124.129.97)  9.203  ms 8.003  ms 11.307 ms
 5  12.122.82.241 (12.122.82.241) 52.901  ms 53.619  ms 51.215 ms
 6  tbr2-p013501.sl9mo.ip.att.net (12.122.11.121)
➥51.625 ms 52.166 ms 50.156 ms
 7  tbr2-cl21.la2ca.ip.att.net (12.122.10.14)
➥50.669 ms 54.049 ms 69.334 ms
 8  gar1-p3100.lsnca.ip.att.net (12.123.199.229)
➥50.167 ms 48.703 ms 49.636 ms
 9 * * *
10  border20.po2-bbnet2.lax.pnap.net
➥(216.52.255.101)  59.414 ms  62.148 ms  51.337 ms
11  intelenet-3.border20.lax.pnap.net
➥(216.52.253.234)  51.930 ms  53.054 ms  50.748 ms
12  v8.core2.irv.intelenet.net (216.23.160.66)
➥50.611 ms  51.947 ms  60.694 ms
13 * * *
14 * * *
15 * * *

那些* * *是什么意思?表明在那一跳(hop)发生了一次5秒种的超时。有时这也表明某台计算机只不过是因为bug而不知道如何处理traceroute数据包,但一连串的星号(*)则说明从v8.core2.irv. intelenet.net开始,数据包转发到的那些路由器在某些地方出了问题。如果一直有问题,就需要通知v8.core2.irv.intelenet.net的管理员,让他知道从这个主机开始的路由出了问题(当然,让gar1-p3100.lsnca.ip.att.net的管理员知道,从该主机到border20. po2-bbnet2.lax.pnap.net的路由也有点问题,但还不像intelenet.net主机的问题那么严重,让他知道这些情况也没什么坏处)。

应对有问题的tracerouter命令的另一种方法是增加命令尝试的最大跳数。在默认情况下,跳数的最大值是30。不过,可以使用–m选项来修改这一设置,例如traceroute -m 40 www.bbc.co.uk

提示 事实上,traceroute的一个更好的替代品是mtr(代表Matt's traceroute)。可以认为它是pingtraceroute的组合。如果你的Linux分发版可以使用mtr,那么下载并试试它吧。更多信息,可以访问www.bitwizard.nl/mtr

host

DNS(Domain Name System,域名系统)的建立使得人们可以更容易地访问因特网上的各种资源。毕竟,计算机可以完美地处理数字,它所做的每一件事实际上都可以表示为数字,而人类则更擅长记忆和处理文字信息。一个网站的IP地址可能是72.14.203.99,但大多数人都很难记住这么长串数字。而要访问www.google.com,记忆这个名字就容易得多了。DNS基本上就是一个巨大的数据库,记录了72.14.203.99 和www.google.com之间的对应关系,以及其他数百万的IP地址和域名的数据。

提示 DNS是一个巨大的、复杂的、引人入胜的话题。有关它的更多细节,可以先看看Wikipedia的“Domain Name System”(http://en.wikipedia.org/wiki/Dns),然后再阅读一下Paul Albitz 和Cricket Liu合著的DNS and BIND

要快速获得和某个域名关联的IP地址,可以使用host命令,如下所示:

$ host www.granneman.com
www.granneman.com is an alias for granneman.com.
granneman.com has address 216.23.180.5
www.granneman.com is an alias for granneman.com.
www.granneman.com is an alias for granneman.com.
granneman.com mail is handled by 30 bhoth.pair.com.

得到5个响应,因为host命令执行了几种DNS查询。不过,从结果中看到想要的信息非常容易:www.granneman.com 的IP地址是216.23.180.5。

也可以做相反的操作,找出与某个IP地址关联的域名,如下所示:

$ host 65.214.39.152
152.39.214.65.in-addr.arpa domain name pointer web.bloglines.com.

说明 很多其他命令都可以找到主机的IP地址,但host命令是完成这一任务最有效的方法。毋庸置疑,用host命令也可以进行相反的查询,而其他命令就不一定总可以这样了。

14.13节还会介绍更多关于如何用host命令帮助解决问题的内容。

ifconfig

在14.1节中,介绍了如何用ifconfig来获得网络接口的状态。不过,ifconfig命令更为强大的功能是能够配置网络接口。

说明 用ifconfig能对配置进行相当多的修改,但这里只能演示一小部分(更多的细节,可以参见man ifconfig)。

要将eth0上的以太网卡的IP地址修改为192.168.0.125,可以运行以下命令(几乎所有与ifconfig相关的命令,都需要以root用户来运行):

# ifconfig eth0 192.168.0.125

为了运行某种类型的网络数据包嗅探工具(如强大的Ethereal),需要首先将网卡设置为混杂(promiscuous)模式。在默认情况下,eth0只监听发送给它的特定数据包,但为了嗅探网络上传递的所有数据包,就需要让网卡监听所有数据包,这就是混杂模式,如下所示:

# ifconfig eth0 promisc

设置好混杂模式后,运行ifconfig命令,就能看到这个网卡正在监听它能接收到的所有数据包。看到第四行的PROMISC了吗?

# ifconfig eth0
eth0 Link encap:Ethernet  HWaddr 00:02:8A:36:48:8A
 inet addr:192.168.0.143  Bcast:192.168.0.255 Mask:255.255.255.0
 inet6 addr: fe80::202:8aff:fe36:488a/64 Scope:Link
 UP BROADCAST PROMISC MULTICAST MTU:1500 Metric:1
[Results truncated for length]

在使用完Ethereal后,不要忘记关闭混杂模式。

# ifconfig eth0 -promisc
# ifconfig eth0
eth0 Link encap:Ethernet  HWaddr 00:02:8A:36:48:8A
inet addr:192.168.0.143  Bcast:192.168.0.255 Mask:255.255.255.0
inet6 addr: fe80::202:8aff:fe36:488a/64 Scope:Link
UP BROADCAST MULTICAST  MTU:1500  Metric:1

甚至还能够修改[或“欺骗(spoff)”]网络设备的硬件MAC地址。某些ISP可能会把因特网服务链接到特定的计算机上,为了避开这种企图,通常就需要使用MAC“欺骗”。修改MAC地址时要小心,因为万一出错,就可能与其他网络设备发生冲突,引发问题。如果决定修改MAC地址,首先一定要用ifconfig获得设备的默认MAC地址,以便以后可以把默认的MAC地址再修改回来(顺便说一下,这里的ifconfig命令显示的MAC地址完全是虚构的,所以也没打算再使用该地址)。

# ifconfig eth0 hw ether 00:14:CC:00:1A:00

ifconfig命令是使用网络接口的基石,务必要理解它的工作原理,这样才能最大限度地利用它提供的全部功能。

iwconfig

ifconfig命令可以显示网络接口的状态,包括无线网卡。然而,它不能显示关于无线网络接口的全部数据,因为ifconfig根本不知道这些信息。要尽可能多地得到与无线网卡相关的数据,应该用iwconfig代替ifconfig

$ iwconfig
lo   no wireless extensions.
eth0 no wireless extensions.
ath0 IEEE 802.11g ESSID:"einstein"
Mode:Managed  Frequency:2.437 GHz Access Point: 00:12:17:31:4F:C6
 Bit Rate:48 Mb/s   Tx-Power:18 dBm Sensitivity=0/3
 Retry:off   RTS thr:off   Fragment thr:off
 Power Management:off
 Link Quality=41/94   Signal level=-54 dBm Noise level=-95 dBm
 Rx invalid nwid:1047   Rx invalid crypt:0 Rx invalid frag:0
 Tx excessive retries:73  Invalid misc:73 Missed beacon:21

从上面的结果可以看到,iwconfig命令能够提供一些无线网络接口特有的数据,包括网卡的类型(这里802.11g)、ESSID或网络名称(这个网络的ESSID是einstein)、连接使用的网络模式或类型、无线访问接入点的MAC地址(这里是00:12:17:31:4F:C6),以及关于无线连接质量的各种详细信息。

通过ifconfig和iwconfig,可以得到有关无线网络接口的所有信息。并且,与ifconfig能够配置有线网卡一样,也可以用iwconfig来配置无线网卡,这正是下一节将要介绍的内容。

iwconfig

在14.6节中,使用iwconfig命令来查看有关无线网卡及其连接的重要细节信息。不过,也可以用iwconfig来配置无线网卡及其连接。如果这些听起来有点像ifconfig,那就对了,因为iwconfig就是基于ifconfig及其行为的。

说明 用iwconfig能够进行一些配置修改,但这里只能看到为数不多的几个(更多的细节,可以参见man iwconfig)。

相当长的一段时间以来,有线网络的拓扑结构(例如,星型、总线型、环型,仅举几个例子)已经为人们所熟知和理解。而无线网络则引人了一些新的拓扑结构,包括以下几种常见的拓扑结构。

  • Managed(一个接入点创建一个网络,其他无线设备可以连接到这个网络,这是一种最常见的无线网络拓扑结构)。

  • Ad-Hoc(两个或更多的无线设备组成一个网络,彼此协同工作)。

  • Master(无线设备相当于一个接入点)。

  • Repeater(无线设备将数据包转发到其他无线设备)。

无线网络的拓扑结构还有其他类型,但以上列出的是主要的几种。可以使用iwconfig设置无线网卡,让它根据新的拓扑结构,以不同的模式来运行。

提示 有关星型、总线型、环型等拓扑结构的更多信息,可以参见Wikipedia的“Network Topology”(http://en.wikipedia.org/wiki/Network_topology)。

# iwconfig ath0 mode ad-hoc

指定接口以后,再简单地使用mode选项,后面跟着想要使用的拓扑模式的名称。

说明 记住,这个例子中使用的网卡的接口名称是ath0;但你的网卡接口名称可能是etH1wlan0或其他完全不同的东西。要查找网络接口的名称,可以只使用iwconfig命令,如前一节所述。

ESSID(Extended Service Set Identifier,扩展服务区标识符)是你已经加入的或想加入的无线网络的名称。在大多数时候,假设无线网络的其他需求都能满足(例如加密,如果有必要的话),那么任意的ESSID名称都可以很好地工作。不过,有些网络可能需要你指定确切的ESSID。

# iwconfig ath0 essid lincoln

这样就意味着你正在加入一个ESSID为lincoln的无线网络。只使用essid选项,后面跟着ESSID的名称,就可以了。

越来越多的网络正在使用加密来保护用户之间的通信,以免被嗅探器捕获所有的网络流量后,从中查看是否存在有用的信息。无线网络中最简单的网络加密形式是WEP(Wired Equivalent Privacy,有线等效加密)。虽然这种加密机制被认为非常缺乏安全性,但它的特点是简单。WEP很容易被知识渊博的攻击者破解,现在已经被更加健壮的WPA(Wi-Fi Protected Access)协议取代。遗憾的是,在Linux中的无线网卡上使用WPA可能非常复杂,已经超出了本书的范围。此外,虽然WEP有缺陷,但仍然很通用,有点安全保护总比没有的好,只是不要奢望用它能提供十全十美的安全保护。

提示 有关WEP和WAP的更多信息,可以参见Wikipedia的“Wired Equivalent Privacy”(http://en.wikipedia.org/wiki/WEP)和“Wi-Fi Protected Access”(http://en.wikipedia.org/wiki/Wi-Fi_Protected_Acces)。有关在Linux系统中使用WPA的信息,可以参见“Linux WPA/WPA2/IEEE 802.1X Supplicant”(http://hostap.epitest.fi/wpa_supplicant)。如果正在通过ndiswrapper①来使用Windows的驱动程序,也可以看看“How to Use WPA with ndiswrapper” http://ndiswrapper.sourceforge.net/mediawiki/index.php/WPA)。

① ndiswrapper实际上是一个开源的驱动程序,它能够让Linux使用标准的Windows下的无线网络驱动程序。——译者注

WEP使用一个共享的密钥(密码)对无线访问接入点和客户端计算机传输的数据进行加密。密码有两种格式:16进制数字和纯文本。其实哪种格式并不重要,因为iwconfig能够处理这两种格式。如果要使用16进制数字,只需要在enc选项后面给出具体的密钥。

# iwconfig ath0 enc 646c64586278742a6229742f4c

如果要使用纯文本格式,仍然需要使用enc选项,但是必须在密钥前面加一个s,表明后面的是一个文本字符串。

# iwconfig ath0 enc s:dldXbxt*b)t/L

提示 创建这些WEP密钥,使用的是一个非常好用的WEP密钥生成器(WEP Key Generator),可以在www.andrewscompanies.com/tools/wep.asp找到它。

如果要同时修改几个选项,用一个命令完成所有的修改可能会比较好。为此,在iwconfig后面跟上设备的名称,然后再依次输入需要修改的各个选项。

# iwconfig ath0 essid lincoln enc 646c64586278742a6229742f4c

上面这条命令为无线设备ath0修改了它的ESSID,并用16进制数字设置了它的WEP密钥。只要你愿意,可以一次设置任意多的选项内容。

dhclient

大部分家庭网络和许多商业网络都使用DHCP(Dynamic Host Control Protocol,动态主机控制协议)为新加入的机器分配IP地址和其他关键信息。如果没有HDCP,在有新加入的机器时,就必须为其手工设置所有的网络信息;有了DHCP,只要把新机器连入网络,再请求DHCP服务器给它提供一个IP地址和其他必需的配置信息,接着就可以自动把DHCP服务器的响应集成到新设备的网络配置中。

说明 以下讨论假设已经用DHCP(而不是手工设置)配置好了网络设备。各种版本的Linux系统会在不同的配置文件中查找DHCP信息。基于Debian的Linux系统会在/etc/network/ interfaces中查找iface [interface] inet dhcp。而基于Red Hat的Linux系统则在/etc/sysconfig/network-scripts/ ifcfg-[interface]中查找BOOTPROTO=dhcp。在这些例子中,应该将[interface]替换为实际的网络接口名称。要得到更多的信息,可以在Google上搜索“dhcp your-distro”(your-distro是你正在使用的Linux版本名称)。

有时,你的计算机不能在启动时连接到DHCP服务器,这样就需要手工发起DHCP请求。或者你的网络有问题,因而需要一个新的IP地址。无论什么原因,dhcilent命令都会尝试查询任何可用的DHCP服务器,向它们请求必要的数据(必须以root用户来运行dhclient)。

#  dhclient eth0

Listening on LPF/eth0/00:0b:cd:3b:20:e2
Sending on   LPF/eth0/00:0b:cd:3b:20:e2
Sending on   Socket/fallback
DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 8
DHCPOFFER from 192.168.0.1
DHCPREQUEST on eth0 to 255.255.255.255 port 67
DHCPACK from 192.168.0.1
bound to 192.168.0.104 -- renewal in 37250 seconds.
#  ifconfig eth0
eth0 Link encap:Ethernet  HWaddr 00:0B:CD:3B:20:E2
inet addr:192.168.0.104  Bcast:192.168.0.255  Mask:255.255.255.0
 inet6 addr: fe80::20b:cdff:fe3b:20e2/64 Scope:Link

要释放(或放弃)DHCP服务器分配的IP地址,可以使用-r(代表release)选项,如下所示:

# dhclient -r eth0

sit0: unknown hardware address type 776
sit0: unknown hardware address type 776
Listening on LPF/eth0/00:0b:cd:3b:20:e2
Sending on   LPF/eth0/00:0b:cd:3b:20:e2
Sending on   Socket/fallback

在理想情况下,当启动计算机、插入无线PCMCIA卡,或是将以太网线连接到有线网卡的插座时,dhclient命令应该自动运行;但有时dhclient无法自动运行。当DHCP不能正常工作时,就得借助dhclient命令了。dhclient非常不错的一点就是它会自动显示很多详细信息,这样你就能够对它的运行情况一目了然,按照需要进行相应的诊断。

说明 一些Liunx分发版仍然在使用一种比较古老的程序执行DHCP(pump),而不是使用dhclient。有关pump命令的信息,可以看一下man pump,或者是阅读Red Hat和Mandrake的“HOWTO”说明(www.faqs.org/docs/Linux-mini/DHCP.html#REDHAT6)。

ifup

其实你一直都在使用ifup命令,只是没有意识到它而已。启动计算机,结果发现已经成功连接到因特网,这时就应该感谢ifup。如果把以太网线插入Linux计算机背后的网卡接口,几秒钟后你就又可以接收电子邮件,这正是因为ifup在幕后做了大量工作。本质上,当ifup检测到网络事件(例如,机器重启、或插入网线)时,它就会开始运行,接着再执行网络配置文件中的指令(如果你感到好奇,上一节中的“说明”中介绍了这些文件的名称和保存位置)。

不过,当偶尔遇到网络问题时,就得手工运行ifup命令了。这相当容易:以root用户登录,在ifcup命令后面输入你想启动的网络接口的名字,如下所示:

#  ifconfig
lo Link encap:Local Loopback
   inet addr:127.0.0.1  Mask:255.0.0.0
...
# ifup eth0
#  ifconfig
eth0 Link encap:Ethernet  HWaddr 00:0B:CD:3B:20:E2
inet addr:192.168.0.14  Bcast:192.168.0.255 Mask:255.255.255.0
 inet6 addr: fe80::20b:cdff:fe3b:20e2/64 Scope:Link
 UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
...

lo   Link encap:Local Loopback
 inet addr:127.0.0.1  Mask:255.0.0.0

注意,如果ifup执行成功,它不会报告任何信息。其实,和大多数Unix应用程序一样,如果执行成功,ifup就保持沉默;只有遇到失败或错误时,它才会报告相关的信息。为了检查ifup是否成功完成,可以使用ifconfig命令,如前面例子演示的那样。

说明 也可以使用ifconfig [interface] upiwconfig [interface] up启动有线或无线网络连接。

ifdown

ifup命令用于启动网络连接,而ifdown命令则用于关闭网络连接。为什么需要关闭网络连接呢?在大多数情况下,这是因为你正在尝试启动某个网络连接,而且ifconfig也报告它已经打开,但发现其配置有问题。所以得先关闭这个网络连接,再打开它,如下所示:

# ifup eth0
ifup: interface eth0 already configured
# ifdown eth0
# ifup eth0

注意,ifdownifup一样,在运行成功时不会提示任何信息。如果输入ifdown命令后,没有看到任何信息,则说明命令运行成功了,相应的网络接口不再工作了。

说明 也可以使用ifconfig eth0 downiwconfig ath0 down关闭有线或无线网络连接。

route

当使用SSH(Secure Shell)连接局域网内的其他计算机时(将在第15章中介绍),计算机是怎么知道如何把数据包限制在LAN内,而不是把它们发送到路由器,再发送到因特网上呢?当通过浏览器访问www.ubuntu.com时,Linux系统是如何知道把请求发送到路由,而不是相邻的另一台计算机呢?

答案是:Linux内核通过路由表来保持跟踪这些信息。要查看当前的路由表,只需要在shell中输入route。(查看路由表不需要root权限,而对其进行修改则需要root权限,这是下一节将要介绍的内容。)如下所示:

$ route
Kernel IP routing table
Destination Gateway     Genmask       Flags Metric
➥Ref       Use Iface
192.168.0.0 *           255.255.255.0 U     0     0     0 eth0
default     192.168.0.1 0.0.0.0       UG    0     0     0 eth0

说明 在这台机器上仅有一个网卡,所以这个路由表相当简单。在同时安装了以太网卡和无线网卡的笔记本电脑上,将会看到其他一些条目。

IP地址由4个八位元组(octet)构成,其格式可以表示为xxx.xxx. xxx.xxx,如192.168.0.124。当从你的机器发送一个数据包时,就会把这个数据包的目标IP地址和路由表中的Destination (目标)列进行比较。Genmask(子网掩码)列用于标识应该检查IP地址4个八位元组中的哪一部分,并和Destination 列一同决定数据包的目标网络地址。

例如,假设在shell中输入ping 192.168.0.124。子网掩码(Genmask)255.255.255.0表明只有最后一个八位元组(由0表示的那个八位组)是重要的。换句话说,当遇到192.168.0.124这样的IP地址时,只有.124这部分对于将数据包路由到正确的主机地址才是重要的。向IP地址从192.168.0.1到192.168.0.255(这个网段IP地址的最大范围)发送的任何数据包都可以匹配Genmask 和 Destination,于是它们会呆在局域网中,不会转发给路由器。这也就是为什么在192.168.0.0旁边的Gateway列有一个星号(*)的原因:不需要通过网关(Gateway),因为是局域网本地流量。

另一方面,其他数据包都默认发送到路由器(在这个例子中是Gateway列的192.168.0.1)。该行的掩码(Genmask列)是0.0.0.0,这表明任何不匹配从192.168.0.1 到192.168.0.255的目标IP地址都将通过192.168.0.1进行发送(因为192.168.0.1是网关地址,这是一种特殊情况)。72.14.203.99、82.211.81.166以及216.23.180.5都匹配0.0.0.0,所以它们必须通过网关访问外网。

route命令提供的路由表的另一个有趣的地方是它的Flags(标志)列,该列提供关于路由本身的一些信息。标志有几种,但最常见的是U(表明路由已启动并可用)和G(表明路由使用网关)。从前面的表中可以看到,两个路由都已经启动,但只有第二个是网关。

route

route命令不仅能够用于显示路由表,还可以对它进行修改。不过,修改时要小心,否则可能破坏网络,使你的计算机上不了网。

假设你的计算机一直与网关连接不畅,任何数据包都不能有效地离开局域网访问因特网(我的系统曾经真的发生过这种情况)。运行route命令,确认网关真的是找不着了,然后使用route命令把网关添加到路由表中(虽然普通用户可以查看路由表,但对它进行修改则需要具有root权限)。

# route
Kernel IP routing table
Destination Gateway Genmask       Flags Metric Ref  Use Iface
192.168.0.0 *       255.255.255.0 U     0      0      0 eth0
# route add -net default gw 192.168.0.1 dev eth0
# route
Kernel IP routing table
Destination Gateway     Genmask       Flags Metric
➥Ref   Use Iface
192.168.0.0 *           255.255.255.0 U     0     0     0 eth0
default     192.168.0.1 0.0.0.0       UG    0     0     0 eth0

现在分析一下这个命令。add表示正在添加一个新的路由(要删除路由,则用del)。-net选项告诉内核,正在添加的目标是一个网络,在这个例子中是default目标。gw表示想使用位于192.168.0.1的网关对匹配目标(这里是default,因此使用子网掩码0.0.0.0)的数据包进行路由。最后,dev eth0指定要使用的设备,在这个例子中使用的是eth0上的以太网卡。

假设除了以太网卡eth0,你还有块无线网卡ath0,想通过该无线网卡访问局域网10.1.xxx.xxxx(该LAN的网络基地址)中的资源,但根本不想让这个无线网卡访问因特网。可以使用以下命令增加一条匹配这些条件的路由规则:

# route
Kernel IP routing table
Destination Gateway     Genmask        Flags Metric
➥Ref    Use Iface
192.168.0.0 *           255.255.255.0  U     0     0     0 eth0
default     192.168.0.1 0.0.0.0        UG    0     0     0 eth0
# route add -net 10.1.0.0 netmask 255.255.0.0 dev ath0
# route
Kernel IP routing table
Destination Gateway     Genmask        Flags Metric
➥Ref    Use Iface
192.168.0.0 *           255.255.255.0  U     0     0     0 eth0
10.1.0.0    *           255.255.0.0    U     0     0     0 ath0
default     192.168.0.1 0.0.0.0        UG    0     0     0 eth0

以上命令先用dev ath0指定无线网卡,再将子网掩码设置为255.255.0.0,以便可以正确地进行路由。如果以后又想删除这个路由,则使用以下命令:

# route
Kernel IP routing table
Destination Gateway     Genmask       Flags Metric
➥Ref   Use Iface
192.168.0.0 *           255.255.255.0 U     0     0     0 eth0
10.1.0.0    *           255.255.0.0   U     0     0     0 ath0
default     192.168.0.1 0.0.0.0       UG    0     0     0 eth0
# route del -net 10.1.0.0 netmask 255.255.0.0 dev eth0
# route
Kernel IP routing table
Destination Gateway     Genmask       Flags Metric
➥Ref   Use Iface
192.168.0.0 *           255.255.255.0 U     0     0     0 eth0
default     192.168.0.1 0.0.0.0       UG    0     0     0 eth0

所有命令都一样,除了使用的是del,而不是add。啊,真容易!

在网络方面,Linux系统通常可以正常工作,但偶尔也会出现问题。以下给出了一些解决网络问题的基本技巧。

如果网络接口看起来已经启动和运行,但是不能访问因特网,这时可以先试着ping一下localhost设备(127.0.0.1)。如果ping不通,就停下来,不要再试了,因为你的系统已经严重损坏了。如果能ping通,就继续ping一下计算机的外部IP地址。如果ping不通这个外部IP,就检查一下计算机是否已经启动了网络。如果能ping通这个外部IP,就再ping一下网络中的其他计算机(假设有其他计算机)。如果没有成功,就是网络接口出了问题(假设路由器是正常的)检查一下网线是否插好了。使用ifconfig(如果是无线网卡,则使用iwconfig)检查一下网络接口的状态,如果必要的话,可以用ifup命令打开网络接口。之后再试着ping一次。

如果能ping通本地的其他计算机,接下来就ping一下路由器。如果能ping通网络上的其他计算机,但是不能ping通路由器,这时就应该用route命令检查路由表(参见14.11节)。如果路由表有找不到的项目,则补充完整丢失的内容(详情可以参考“改变路由表”)。

说明 如果有一个可以参考的基准,那么诊断和修复问题将会容易得多。找台网络正常的计算机,运行route命令,保存结果,这样,如果以后路由表出了问题,需要恢复什么时,也可以有个参考。

如果能够ping通路由器,试着ping一个你知道在因特网上启动并正在运行的主机(如www.google.com 或www.apple.com )。如果ping不通路由器,则尝试再ping一下同一计算机的IP地址。为了在这样的场合下使用,最好事先把一些常用IP地址记在便条上或是计算机的文本文件中以备后用。表14-1列举几个目前还不错的网站及其IP。当然,它们可能会发生变化,所以应该自己查一查。

表 14-1
网  站 IP 地址
www.google.com 72.14.203.99
www.apple.com 17.254.0.91
www.ubuntu.com 82.211.81.166
www.ibm.com 129.42.16.99
www.granneman.com 216.23.180.5

说明 如何得到这些IP地址呢?可以使用某个域名来ping相应的主机,ping就会提供它的IP地址,或者使用traceroute也可以得到相同的信息。一种更快的方法是使用host命令,在14.4节中介绍过这个命令。

如果能ping通IP地址,但ping不通它的域名,那就是有DNS问题了。如果正在使用DHCP,可以运行dhclient命令(参见14.8节)更新DHCP服务器提供的DNS信息。如果你没有使用DHCP,那么为了找到需要的DNS信息,可以查询路由器,也可以向管理员或ISP服务商咨询,然后以root用户的身份,手工在/etc/resolv.conf中添加新的DNS信息,如下所示:

nameserver 24.217.0.5
nameserver 24.217.0.55

每行以nameserver作为开始(必须的),后面是想用作DNS的IP地址。如果路由器支持DNS,而且也知道它的IP地址(假设是192.168.0.1),那么第一行可以试着写成:

nameserver 192.168.0.1

试着运行ifdown,再运行ifup,看看网络是否正常。如果仍然有问题,重新开始,这时通常要从硬件着手。所有硬件安装正确吗?所有硬件插好了吗?在确保这些都正常以后,开始检查软件。最坏的情况是硬件没有Linux版本的驱动程序。虽然这种情况不多见,而且以后会越来越少见,但仍然可能碰巧遇到。

但是,无线网卡可能与Linux非常不兼容,因为保守的硬件制造商并不想帮助Linux开发人员让他们的硬件在Linux系统中工作。为了避免遇到这些令人头疼的问题,在购买无线网卡之前,最好先查看它的网站,确保这种网卡在Linux系统中能很好地工作。这方面的一些优秀网站包括:Madwifi的硬件支持网站(http://madwifi.org/wiki/Compatibility);Linux无线局域网支持网站(http://linux-wless.passys.nl/),这个网站虽然有些过时,但还是依然有用;还有一个网站(http://del.icio.us/rsgranne/wireless),经常在这里更新有关Linux和无线网络连接的书签。对于特定的硬件,推荐使用Netgear WG511T无线PCMCIA卡。在运行最新版本的K/Ubuntu的计算机上,只需把这种网卡插入到计算机中,它就马上能工作了。

噢,解决问题的最后一搏:如果能成功ping通IP地址和域名,那就看到这儿吧。能上网了!玩得开心些吧!

本章介绍了很多网络工具,其中的大部分(例如,ifconfigiwconfig以及route)都有双重用途:既能提供有关网络连接的信息,也能修改网络连接的配置参数。其他的一些命令主要是用于诊断目的,例如pingtraceroutehost。最后一些命令则用于控制能否连接到网络:dhclientifupifdown。如果认真对待Linux,那么就需要把这些命令都用心学好。没有什么比网络连接无法正常工作更让人沮丧的了,但如果选对了工具,解决问题通常很简单。当出现问题时,对这些工具越了解,才能越迅速而有效地解决问题。



第15章 使用网络

本章介绍的许多命令都有丰富的特性和功能,足以写上好几本书。本书并没有介绍有关使用sshrsyncwgetcurl命令的一些比较复杂的内容,但是教你学会使用网络。学习这些命令最好的方法就是先试用它们,等熟悉了基本用法以后,再去探索感兴趣的其他领域。不过,本章介绍的这些内容就足够你忙乎一段时间了,展开想象的翅膀,你会发现本章介绍的这些工具的应用天地竟然如此广阔,真酷!

ssh

因为Unix的构建思想就是联网,所以开发人员很早就创建出了允许用户连接到其他计算机的程序,让他们能够运行程序、查看文件和访问资源,这一点也不奇怪。长期以来,联网使用的程序一直是telnet,但它存在一个非常大的问题:telnet完全是不安全的。用telnet发送的所有东西(包括用户名、密码及命令和数据),没有经过一点加密就直接发送了出去。能监听到这些信息的任何人都可以看到发送的所有东西,这样真不安全。

为了克服这个问题,ssh(secure shell)应运而生。它可以完成telnet能做的所有事情,甚至更多。更好的是,ssh的所有流量都经过加密,这让ssh功能更强大、也更有用。如果需要将计算机连接到其他计算机,无论另一台计算机是在地球的另一边,还是就在隔壁房间,都可以使用ssh

假设为了查看某个文件,现在想用ssh命令,把笔记本电脑(计算机名为pound,IP地址为192.168.0.15)连接到台式机上(计算机名为eliot,IP地址为192.168.0.25)。你在笔记本上的用户名是ezra,而台式机上的用户名是tom。为了SSH到eliot,需要输入以下命令[也可以使用域名(如果存在),例如hoohah.granneman.com]:

$ ssh tom@192.168.0.25
tom@192.168.0.25's password:
Linux eliot 2.6.12-10-386 #1 Mon Jan 16 17:18:08 UTC
➥2006 i686 GNU/Linux
Last login: Mon Feb 6 22:40:31 2006
➥from 192.168.0.15
[Listing truncated for length]

在连接之后,会提示输入密码。直接输入密码[你看不到输入的内容,这样可以防止某些人“肩窥(shoulder surfing)①”你的操作,窃取到你的密码],再按Enter键。如果密码被接受,就会看到显示一些有关刚才正在连接到的计算机的信息,包括计算机名称、内核、日期和时间及最后一次登录的时间。现在可以运行有权在那台计算机上可以运行的任何命令,好像你就坐在那台计算机面前一样。从ssheliot的角度来看,你到底在哪并不重要——你已经登录了,而且准备好运行命令了。

① 肩窥(shoulder surfing)指犯罪者隐藏在自动提款机附近偷窥持卡人输入密码等信息。——译者注

不过,如果是第一次连接到eliot,显示的信息会略有不同:

$ ssh tom@192.168.0.25
The authenticity of host '192.168.0.25
➥(192.168.0.25)'  can't be established.
RSA key fingerprint is 54:53:c3:1c:9a:07:22:0c:82:
➥7b:38:53:21:23:ce:53.
Are you sure you want to continue connecting
➥(yes/no)?

基本上,ssh是在告诉你它不能识别这台计算机,要求验证计算机的身份。输入yes,再按Enter键,就会得到另一条信息,同时要求输入密码,如下所示:

Warning: Permanently added '192.168.0.25'  (RSA)
➥to the list of known hosts.1
tom@192.168.0.25's password:

从这时以后,处理就都一样了。当第一次连接到eliot时才会看到这条消息,这是因为ssh此时会将涉及的RSA密钥指纹保存到pound上的~/.ssh/known_hosts目录下。查看这个文件,你会发现其中新增加了一行。依赖于在/etc/ssh/ssh_config文件中是否开启了HashKnownHosts选项,新增加的这一行可能采用两种格式中的一种。如果将HashKnownHosts设置为no,这一行将类似于以下这个样子:

192.168.0.25 ssh-rsa SkxPUQLYqXSzknsstN6Bh2MHK5AmC6E
➥pg4psdNL69R5pHbQi3kRWNNNNO3AmnP1lp2RNNNNOVjNN9mu5FZe
➥l6zK0iKfJBbLh/Mh9KOhBNtrX6prfcxO9vBEAHYITeLTMmYZLQHB
➥xSr6ehj/9xFxkCHDYLdKFmxaffgA6Ou2ZUX5NzP6Rct4cfqAY69E
➥5cUoDv3xEJ/gj2zv0bh630zehrGc=

除了加密的散列(hash)代码,还可以清楚地看到IP地址。但是如果将HashKnownHosts设置为yes,就找不到原来你看到的那些信息了。

NNNNO3AmnP1lp2RNNNNOVjNNNVRNgaJdxOt3GIrh00lPD6KBIU1k
➥aT6nQoJUMVTx2tWb5KiF/LLD4Zwbv2Z/j/0czCZIQNPwDUf6YiKU
➥FFC6eagqpLDDB4T9qsOajOPLNinRZpcQoPlXf1u6j1agfJzqUJUY
➥E+Lwv8yzmPidCvOuCZ0LQH4qfkVNXEQxmyy6iz6b2wp=?

所有的信息都经过散列处理,包括机器的IP地址和域名。从安全角度来看,这种形式很好,但如果eliot上的OS变了,就会有问题了。换句话说,如果重新安装了eliot上的Linux操作系统,在下一次再从pound机子上登录时,就会看到以下恐怖的警告信息:

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY
Someone could be eavesdropping on you right now
➥(man-in-the-middle attack)!
It is also possible that the RSA host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
19:85:59:5c:6a:24:85:53:07:7a:dc:34:37:c6:72:1b.
Please contact your system administrator.
Add correct host key in /home/pound/.ssh/
➥known_hosts to get rid of this message.
Offending key in /home/pound/.ssh/known_hosts:8
RSA host key for 192.168.0.125 has changed and you 
➥have requested strict checking.
Host key verification failed.

问题是由于重新安装了eliot上的操作系统,它的ssh密钥也随之发生了变化,当ssh用新的密钥来检查它原来在poundknown_hosts文件中保存的eliot的密钥时,就会发现二者不匹配,并发出警告。要解决这个问题,只要删除poundknown_hosts文件中相应于eliot的那一行,再重新连接。对于ssh,这相当于第一次进行连接,所以它还会询问你是否接受密钥。当然还是回答yes,一切恢复正常了。

如果用SSH只连接过eliot这一台计算机,那么很容易就能够在known_hosts中找到并删除正确的行,因为这个文件中只有唯一的一行。但是如果用SSH连接过多台计算机,要找到正确的行就有些困难了。例如,下面这些行中,哪一行代表hoohah.granneman.com的数据呢?

  • AAAAB3NzaC1yc2EAAAABIwAAAIEAtnWqkBg3TVeu00yCQ6XOVH1xnG6aDbWHZIGk2gJo5XvS/YYQ4Mjoi2M/w/0pmPMVDACjQHs6LvXHSSP6rntdcYQQO4G9dfBnwBCYAvaEMcpDbCyKs1h6w1ntsWmdHWHLR+Yji8lmzCvqPiBhPM0YDU4dsxIAKRDkzll6vm6o2jc=
  • NNNNO3AmnP1lp2RNNNNOVjNNNVRNgaJdxOt3GIrh00lPD6KBIU1kaT6nQoJUMVTx2tWb5KiF/LLD4Zwbv2Z/j/0czCZIQNPwDUf6YiKUFFC6eagqpLDDB4T9qsOajOPLNinRZpcQoPlXf1u6j1agfJzqUJUYE+Lwv8yzmPidCvOuCZ0LQH4qfkVNXEQxmyy6iz6b2wp=
  • AAAAB3NzaC1yc2EAAAABIwAAAIEAtnWqkBg3TVeu00yCQ6XOVH1xnG6aDbWHZIGk2gJo5XvS/YYQ4Mjoi2M/w/0pmPMVDACjQHs6LvXHSSP6rntdcYQQO4G9dfBnwBCYAvaEMcpDbCyKs1h6w1ntsWmdHWHLR+Yji8lmzCvqPiBhPM0YDU4dsxIAKRDkzll6vm6o2jc=

嗯,有些难判断吧?因为根本无法判断,所以除了删除所有行以外,别无其他选择,这意味着以后登录到任何一台计算机时,都得重新接受密钥。为了避免这样,可能最容易的办法就是以root的身份编辑/etc/ssh/ssh_config文件,并将HashKnownHosts设置为no

ssh

本节的标题看起来有些像是在误导人,但确实可以在不提供密码的情况下而通过ssh登录到其他计算机。如果每天都要登录某台计算机(或许有几台机子,可能一天要登录好多次),本节介绍的技术就能让你非常满意。

假设现在想从pound(用户名为ezra)登录到eliot(用户名为tom),而且不需要输入任何密码。首先,用以下命令在pound上创建一个ssh身份验证密钥(authentication key):

$ ssh-keygen -t dsa
Generating public/private dsa key pair.
Enter file in which to save the key (/home/ezra/.ssh/id_dsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in/home/ezra/.ssh/id_dsa.
Your public key has been saved in/home/ezra/.ssh/id_dsa.pub.
The key fingerprint is:
30:a4:a7:31:27:d1:61:82:e7:66:ae:ed:6b:96:3c:24 ezra@pound

按Enter键,接受密钥要保存到的默认位置,密语(passphrase)①为空,此时将提示按两次Enter键。这样就在~/.ssh/id_dsa文件中创建了一个私钥,在~/.ssh/id_dsa.pub文件中创建了一个公钥。

① 密语和密码非常相似,但是密语可以是一句话,里面有单词、标点符号、数字、空格或任何你想要的字符。——译者注

现在需要将公钥(不是私钥)从pound传送到eliot上。ssh开发人员走得比我们要远,他们已经创建了一个程序,让这一操作变得易如反掌。为了将公钥自动从pound复制到eliot,只需要在pound上输入以下命令:

$ ssh-copy-id -i ~/.ssh/id_dsa.pub tom@192.168.0.25

现在试着用ssh 'tom@192.168.0.25'登录eliot,再检查一下.ssh/ authorized_keys,确保这次它没有在这个文件中额外添加你不想要的密钥。

至此已经搞定了(如果你想试试ssh-copy-id命令给出的建议,尽管去试)。看看当使用ssh命令从pound连接到eliot时,会发生什么:

$ ssh tom@192.168.0.25
Linux eliot 2.6.12-10-386 #1 Mon Jan 16 17:18:08 UTC
➥2006 i686 GNU/Linux
Last login: Mon Feb 6 22:40:31 2006 from 192.168.0.15

注意,命令没有要求输入密码,这就是想要的效果。

有些人可能会置疑这种方法的安全性。没有密码?自由地交换密钥?确实是这样的,但是请再思考一下。如果有人此时在pound上,他就可以不用密码而登录eliot了。但这只是意味着需要在pound上实施更好的安全防范措施。如果pound被盗用,无论攻击者是否意识到他可以连接到eliot,都存在大量的安全隐患。在此基础上,你会一直在因特网上到处发送口令。如果攻击者获取了密码,他也能够进行严重的破坏行为。你的私钥不也与密码同样重要吗?你不打算把它备份好,看护好它吗?在考虑到这些方面以后,应该明白通过ssh交换密钥至少和使用密码一样安全,甚至在大多数情况下更安全。

尽管如此,如果喜欢继续使用密码,你肯定有权这么做。这也正是开源和Linux事业的宗旨:提供更多的选择。

sftp

ssh要比telnet好得多一样,SFTP也要比普通的FTP好得多。与telnet类似,FTP也是以透明的方式来发送用户名、密码及传输的所有数据,监听到这些数据包的任何人都可以看明白其内容。相反,SFTP则使用ssh来加密所有东西:用户名、密码及传输的数据。除了SFTP的安全性比FTP高出无数倍以外,SFTP和FTP非常相似,这使学习和使用SFTP都非常容易。

如果能够通过ssh访问某台计算机,那么也一定能够通过SFTP访问到它。要使用sftp命令,从pound(IP地址为192.168.0.15,用户名为ezra)连接到eliot(IP地址为192.168.0.25,用户名为tom),只需使用以下命令:

$ sftp tom@192.168.0.25
Connecting to 192.168.0.25...
tom@192.168.0.25's password:
sftp>

如果读过15.2节,可能会问为什么现在还要提示输入密码。说得对:前面这个例子是没有为连接设置密码的情况。如果设置好了密码,将看到一个类似以下这个样子的登录信息:

$ sftp tom@192.168.0.25
Connecting to 192.168.0.25...
sftp>

通过sftp登录以后,运行的命令就相当标准了。表15-1列出了一些常见的命令,完整的命令介绍,可以查看man sftp

表15-1 有用的SFTP命令
命  令 含  义
cd切换目录
exit关闭与远程SSH服务器的连接
get将指定的文件复制到本机
help获取与命令相关的帮助
lcd将目录切换到本机
lls列出本机上的文件
ls列出远程SSH服务器上当前工作目录中的文件
put将指定的文件复制到远程SSH服务器
rm将指定的文件从远程SSH服务器删除

scp

如果你时间紧,但想安全地把一个文件从一台计算机复制到另一台,scp(表示“secure copy”)就是需要使用的命令。以下是scp命令的基本使用模式:

scp user@host1:file1 user@host2:file2

这种语法格式基本上与传统的cp命令一样,只不过现在延伸到了网络。举个例子将能更明白地说明问题。

假设你想使用scp命令把backup.shpound(IP地址为192.168. 0.15,用户名为ezra)复制到eliot(IP地址为192.168.0.25,用户名为tom)的/home/tom/bin目录,如下所示:

$ pwd
/home/ezra
$ ls ~/bin
backup.sh
$ scp ~/bin/backup.sh tom@192.168.0.25/home/tom/bin
backup.sh              100% 8806     8.6KB/s   00:00
$

这个命令不会提示输入密码,因为在15.2节中已经设置好了,所以ssh现在不需要密码也可以从pound连接到eliot,因为scp依靠的也是ssh,因此在这儿也不需要密码。如果还没有设置好,在命令继续执行前就会要求输入密码。

假设想把几个JPEG图片文件从pound复制到eliot。没有问题,使用通配符就可以了,如下所示:

$ ls -1 ~/covers
earth_wind_&_fire.jpg
handel_-_chamber_music.jpg
smiths_best_1.jpg
strokes_-_is_this_it.jpg
u2_pop.jpg
$ scp *.jpg tom@192.168.0.25:/home/tom/album_covers
earth_wind_&_fire.jpg          100%    44KB  43.8KB/s
handel_-_chamber_music.jpg     100%    12KB  12.3KB/s
smiths_best_1.jpg              100%    47KB  47.5KB/s
strokes_-_is_this_it.jpg       100%    38KB  38.3KB/s
u2_pop.jpg                     100%   9222
9.0KB/sQ

现在,假设你想换个复制的方向。你仍然在pound上,想把Libby的几个图片从eliot复制到pound上,而且还要复制到与当前工作目录不同的另一个目录,如下所示:

$ scp tom@192.168.0.25:/home/tom/pictures/dog/libby*~/pix/libby
libby_in_window_1.20020611.jpg 100%  172KB 172.4KB/s
libby_in_window_2.20020611.jpg 100%  181KB 180.8KB/s
libby_in_window_3.20020611.jpg 100%  197KB 196.7KB/s
libby_in_window_4.20020611.jpg 100%  188KB 187.9KB/s

当需要在计算机之间安全地复制文件时,scp命令真的很有用。不过,如果要复制的文件很多,你很快就会发现使scp还是麻烦。在这种情况下,你可能还是想用SFTP,或者挂载一个Samba共享(这将16.5节中详细介绍)。

rsync -v

rsync是最酷、最有用的命令之一,很多人每天都要依赖它(就像我一样)。它是干什么的呢?它的用途多得数不清(这里再说一遍“关于这个命令可以写本书!”),但本书主要介绍一个非常强大、必需的功能:有效而安全地备份文件,同时付出最少的网络流量。

假设需要在每天半夜从名为coleridge的计算机上(用户名为sam)将2 GB的文件备份至一台名为wordsworth的计算机上(用户名为will)。如果没有rsync,每天夜里你就会看到这2 GB文件的传输,即使是在连接速度很快的网络中,这也是不小的数据传输量。不过,有了rsync,这一传输一小会儿就可以完成。为什么呢?因为在rsync备份这2 GB的文件时,它只传输所有2 GB数据的文件中发生过变化的那些文件。如果在过去的24小时内只有几百KB字节的数据发生了变化,那么这就是rsync命令所有要传输的内容。如果发生变化的数据有100 MB,rsync命令也只复制这100 MB的数据。不管在什么情况下,复制的数据量会大大少于2 GB。

这个命令是这样来用的,从coleridge计算机上运行,将documents目录的所有内容传输到wordsworth计算机的一个备份驱动器上。先看看这个命令和它的运行结果,接着仔细研究一些选项的含意[为了可读性,第一个命令使用了长选项格式,而不是单个字符;第二个命令的选项则使用了单个字符(如果有可用的短选项),以便比较],如下所示:

$ rsync --verbose --progress --stats –recursive
➥--times --perms --links --compress  --rsh=ssh
➥--delete /home/sam/documents/
➥will@wordsworth:/media/backup/documents

$ rsync -v --progress --stats -r -t -p -l -z -e ssh
➥--delete /home/sam/documents/
➥will@wordsworth:/media/backup/documents

当然,如果想把所有选项组合起来的话,也可以按如下方式运行命令:

$ rsync -vrtplze ssh --progress --stats –delete
➥/home/sam/documents/will@wordsworth:/media/backup/documents

在使用以上列出的任何一种方法运行rsync命令以后,将看到类似以下内容的输出:

building file list ...
107805 files to consider
deleting clientele/Linux_Magazine/do_it_yourself/13/
➥gantt_chart.txt~
deleting Security/diebold_voting/black_box_voting/
➥bbv_chapter-9.pdf

deleting E-commerce/Books/20050811 eBay LIL ABNER
➥DAILIES 6 1940.txt
Security/electronic_voting/diebold/black_box_voting/
➥bbv_chapter-9.pdf
legal_issues/free_speech/Timeline A history of free speech.txt
E-commerce/2005/Books/20050811 eBay LIL ABNER DAILIES 6 1940.txt

connectivity/connectivity_info.txt
[Results greatly truncated for length]
Number of files: 107805
Number of files transferred: 120
Total file size: 6702042249 bytes
Total transferred file size: 15337159 bytes

File list size: 2344115

Total bytes sent: 2345101
Total bytes received: 986
sent 2345101 bytes  received 986 bytes  7507.48 bytes/sec
total size is 6702042249  speedup is 2856.69

先看一下这些结果。rsync命令先构建一个它必须要考虑的所有文件的列表(在这个例子中是107 805个文件),接着删除任何在目标位置(wordsworth)已经存在,但在源位置(coleridge)已经不存在的文件。在本例中,删除了3个文件:来自Linux Magazine的一篇文章的备份文件(~是一个附加的字符)、一个关于电子投票的PDF文件及一个购买过的图书的文本收据。

删除完文件以后,rsync就开始复制所有发生过变化的文件,或者如果是同一个文件,但文件的部分内容有变化,rsync会只复制这个文件中发生变化的部分。在本例中,复制了4个文件。结果,那个PDF文件实际上是被移动到了一个新的子目录中,但对于rsync来说,它还是个新文件,所以要完整复制它。

文本收据文件也是如此。“A history of free speech.txt”这个文件是个全新的文件,所以也直接将它复制到wordsworth 。

在列出进行的变化之后,rsync会给出一些有关传输的总体信息。总共传输了120个文件,6702042249 字节(大约6.4 GB)中传输了15337159字节(大约14 MB)。在统计中还包含一些其他数据,但以上的信息是关键数据。

现在看看向计算机发出的命令。命令的首部和尾部比较容易理解:命令以rsync作为开始,后面跟着选项,接着是复制文件的源目录(coleridge计算机上的/home/sam/documents/),随后是复制文件的目标目录(wordsworth计算机上的/media/backup/documents)。在介绍各个选项之前,先重点看看源目录和目标目录的指定方式,这里需要理解清楚,否则会造成实际的损害。

想复制的是coleridgedocuments目录中的内容,但不包括目录本身,所以源目录应该使用documents/,而不是documents。在/home/sam/documents/中,documents后面的斜线(“/”字符)是在告诉rsync命令:想把那个目录的“内容”复制到wordsworth上的documents目录中;如果只用documentsrsync命令复制的就是目录及其内容,结果是将源目录复制到了wordsworth计算机上的/media/backup/documents/documents这个目录中。

说明 斜线只在源目录中重要,在目标目录中是否使用斜线则无关紧要。

有个选项在前面的例子中没有使用,但是当想先估计一下rsync命令将如何构建备份过程时,使用这个选项是个好主意:-n(或--dry-run)。如果命令中包含了这个选项,在rsync运行时,它并不会真正地删除或复制任何东西。如果你的选择可能会导致删除重要的文件,这个选项就是救命的稻草了。在提交rsync命令以前,尤其是当包括了--delete选项时,最好是自己帮自己的忙,执行一个演练操作。

现在轮到介绍前面例子中的选项了。-v(或--verbose)选项,连同--progress,它们命令rsync随时报告它正在进行的操作的细节。在本节前面展示的执行结果中,可以看到rsync告诉你它正在删除什么,以及它正在复制什么。如果是通过自动化的脚本来运行rsync,则不需要使用这个选项,不过,加上也没什么坏处。如果是以命令行交互方式来运行rsync,显示的这些信息则相当有用,因为你能借此明白正在发生的事情。

rsync结果的末尾显示的那些元数据(传输文件的数量和大小及其他有趣的数据),它们之所以出现,是因为在命令中包含了--stats选项。同样,如果在脚本中用rsync,就不需要使用这个选项。但如果是手动运行程序,如果能看到这些信息,确实还是不错的。

-r(或--recursive)选项在其他命令中已经出现多次了,此处它的作用和在其他地方是一样的。加上这个选项后,命令就不会只处理当前目录,它会遍历所有的子目录,处理路径上找到的所有东西。因为想复制整个documents目录及它的所有内容,所以需要使用-r选项。

-t(或--times)选项让rsync在传输文件时保留文件的修改时间。如果没有包含这个选项,rsync就不能判断它以前传输过什么,下一次运行命令时,所有文件会再复制一次。这大概不是你想要的行为,因为它完全背离了rsync的初衷,所以一定要记得包含-t选项。

第7章中讨论的权限,这里又再次出现。-p(或--perms)是告诉rsync要更新目标文件的权限设置,让它们与源文件匹配。这可以让备份文件与源文件尽可能保持一致,所以加上这个选项也是一个不错的想法。当源目录中包含软链接时,-l(或--links)选项就会在目标目录中重建软链接。不必复制实际的文件(这明显不是创建软链接的人的原本意图),只需要复制指向实际文件的链接,这样再一次又保持了源目录的原始状态。

即使具有快速的网络连接,使用-z(或--compress)选项也是个好想法,因为这时rsync将使用gzip来压缩传输的文件。在网速慢的连接中,必须使用这个选项;在快速的连接中,使用这个选项同样也能节省更多的时间。

为了安全,应该使用-e(或--rsh=ssh)选项,告诉rsyncssh对它传输的数据进行加密。文件传输不但容易,而且也安全。应该试一下!

说明 如果使用的是ssh,为什么这里不要求提供密码呢? 因为这里使用的是15.2节中演示的技术,免去了手工输入口令的需要。

最后一个要介绍的选项是--delete。如果你正在创建文件的镜像,显然一定想让镜像与源文件尽可能保持精确。这意味着在源文件中已经被删除的文件,也同样要在目标文件中删除。但这也意味着,你可能意外地删除些原本想保留的东西。如果准备使用--delete选项(估计你会用的),记得一定先用-n(或--dry-run)选项演练一下(在rsync选项介绍的一开始讨论的那个选项)。

rsync命令还有很多其他选项和使用方法(man page列出了8种使用方法,引人入胜)。不过,本节介绍的建立这一命令的方法肯定能够让你开始用这个命令。在显示终端上打开man rsync,可在Google中搜索“rsync tutorial”,都可以找到大量的有用信息。这样想想rsync: 当你正因为误删除了一个文件而大呼“哦,不!”时,马上又想到“哈哈!这个文件原来用rsync备份过了!”。想到这些,你一定会非常乐意花时间学习这个相当丰富而有用的命令吧。

提示 如果想真正保证数据的安全,应该在周期性的cron作业中设置rsync命令自动运行。例如,创建一个名为backup.sh的文件(~/bin是保存这个文件的好地方),在其中输入前面已经用过的命令,如下所示:

  $ rsync --verbose --progress –stats
  ➥--recursive --times --perms –links
  ➥--compress --rsh=ssh –delete
  ➥/home/sam/documents/ will@wordsworth:
  ➥/media/backup/documents

用chmod让这个文件成为可执行的:

  $ chmod 744 /home/scott/bin/backup.sh

接着将以下几行命令添加到一个名为cronfile的文件中(也可以将这个文件放在~/bin目录中):

  # backup documents every morning at 3:05 am
  05 03 * * * /home/scott/bin/backup.sh

第一行是段注释,解释了这一定时任务的目的,第二行告诉cron在每天夜里的3:05 a.m自动运行/home/scott/bin/backup.sh

现在把这个定时任务加到cron,如下所示:

  $ crontab /home/scott/bin/cronfile

现在再也不必为备份文件担心了。它是全自动执行的,只要确保计算机通宵开着就行!

[有关cron的更多信息,可以参阅man cron或文章“Newbie: Intro to cron”(www.unixgeeks.org/security/newbie/unix/cron-1.html)]

wget

网络就像是一个堆满了图片、电影和音乐文件的丰富宝藏,可供人随时下载。问题是对于一个有200多首MP3的文件集,如果让你逐一手工下载每个文件,你很快就会烦了,心情变得糟糕,不断唉声叹气。wget这个命令就是用于无干扰地下载多个文件和Web网站。只要一个操作来设置好命令,它就会高高兴兴地下载指定的东西,可能会持续好几个小时,一直到全部下载完。

当然,最难处理的还是如何建立这个命令。wget也是一个功能超级强大的程序,详细介绍完它的方方面面真得又够写一整本书了,所以这里没有展示它的所有功能,只重点介绍用它可以做的两件事: 下载整个一组文件(本节介绍的内容),以及下载整个网站(下一节将要介绍的内容)。

假设有以下应用场景:你发现了一个非常精彩的网站,叫做“The Old Time Radio Archives”。在这个网站上有很多怀旧经典歌曲,而且提供了MP3格式的下载,多达365首。确切地说,一年中的每一天都可以欣赏一首。如果能把这些MP3文件都抓回来,肯定是好事。但是一想到要在每首MP3文件的超链接上右点击鼠标,选择“目标另存为(Save Link As)”菜单项,再单击“保存(OK)”按钮来开始下载,这么大量烦琐的劳动就不那么吸引人了。

再检查一下这些MP3链接的目录结构,你会注意到这些链接都是按同样的目录进行组织的,如下所示:

http://www.oldtimeradioarchives.com/mp3/
   season_10/
   season_11/
   ...
   season_20/

   ...
   season_3/
   season_4/
   ...
   season_9/

说明 这些目录并没有按数字顺序来排序(人通常是按数字的大小来排序的),而是按字母顺序来排序的,计算机默认对数字就是这样排序的,除非指定其他的排序方式。因此,“10”就排在了“3”的前面。

在每个目录中都包含多个MP3文件,一些目录可能只包含为数不多的几个文件,而另一些目录包含的文件则可能差不多有20个。如果单击链接,进入到目录中,就会打开一个网页,上面列出了这个目录中的所有文件,如下所示:

 [BACK] Parent Directory     19-May-2002 01:03      - 
[SND]  1944-12-24_532.mp3   06-Jul-2002 13:54    6.0M
[SND]  1944-12-31_533.mp3   06-Jul-2002 14:28    6.5M
[SND]  1945-01-07_534.mp3   06-Jul-2002 20:05    6.8M

[SND]是一个音乐符号的GIF图像,在每个文件的前面演示。

所以,现在的问题是:如何下载这些位于不同目录中的具有不同文件名称的所有MP3文件。答案就是wget

先在计算机上创建一个目录,保存要下载的MP3文件。

$ mkdir radio_mp3s

使用cd命令,进入到新创建的那个目录,再运行wget,如下所示:

$ cd radio_mp3s
$ wget -r -l2 -np -w 5 -A.mp3 -R.html,.gif
➥http://www.oldtimeradioarchives.com/mp3/`

接下来逐一介绍这个命令和它的选项。

不用多说,wget是你运行的命令,命令行的最末尾是你希望wget使用的URL:http://www.oldtimeradioarchives.com/mp3。不过,最重要的东西,是位于命令名和URL之间的各个参数。

wget-r(或--recursive)选项会沿着链接,深入到各个子目录中搜索文件。通过告诉wget以递归方式进行下载,可以保证wget遍历每个season_#子目录,抓取回它找到的所有MP3文件。

-l2(或--level=[#])选项不但重要,而且也很微妙。它用于告诉wget在取回文件时最多应该递归多深。小写的字母l在这里代表“level(层)”,数字表示wget应该向下搜索的最大深度。如果将层数指定为-l1wget将只搜索/mp3这一层目录。当然,这一层上什么也下载不到。记住,/mp3目录中还包含其他子目录:season_10season_11等,这些目录才包含你想要下载的MP3文件。通过指定-l2,也就是让wget先进入/mp3(这是第一层目录),然后再依次进入每个season_#目录,抓取回子目录中的任何东西。在指定层数时,要非常小心。稍不留神,就可能在非常短的时间内把硬盘空间塞满。

避免下载过多文件的一种办法是使用-np(或--no-parent)选项,它可以阻止wget递归到父目录。如果回头看看前面列出的文件列表,你会注意到每个链接的第一部分都指向同一父目录。换句话说,在/season_10目录中,它的父目录是/mp3;而/season_11/season_12等各目录的父目录都是/mp3。不过,你应该不希望wget向上搜索,而是希望它只向下搜索。也就是说,在每个season_#目录中,肯定不需要浪费时间让wget向上搜索相同的目录(/mp3)。

接下来的那个选项不是必须的,但是如果使用这个选项一定会让你显得很有礼貌。-w(或--wait=[#])选项可以在每两个文件的下载之间引入一个短暂的时间间隔。这有助于当连续不断地从服务器下载文件时,避免对服务器产生过重的负担。在默认情况下,wget下载间隔数字的单位是秒;如果需要,也可以在数字后面添加一个m,以指定分钟,或是用h指定小时,甚至用d来指定天数。

现在轮到介绍一个非常有趣的选项了。-A(或--accept)用于告诉wget:你只想下载某种类型的文件,而其他文件则不需要下载。A代表accept(接受),后面跟着需要下载的各种文件的后缀名[用逗号(,)分隔]。如果你只想下载一种类型的文件(例如MP3文件),可以这样来指定参数:-A.mp3。

相反,-R(或--reject)选项则是告诉wget不想下载的文件: HTML和GIF文件。排除了这些类型的文件后,wget就不会下载前面展示的[SND]所代表的那些小音乐符号的图片文件。排除文件的后缀列表也是用逗号(,)分隔的,例如-R.html,.gif

运行带有这些选项的wget命令,就会把你想要的365首MP3文件全部下载到计算机中。如果因为某些原因(例如,路由器停止工作了;有人绊到了以太网线上,把它从电脑上拉开了;挖掘机铲断了公司的光纤),wget传输中断了,这时只要重复命令,并加上-c(或--continue)选项。这个选项告诉wget接着下载没有下载完的文件。这样就不必再重新下载所有文件。

下面再举个用wget下载文件的例子。London DJ发行了两部MP3专辑,由Beatles(甲壳虫乐队)和Beastie Boys这两个乐队的作品组成。当然,我们要听Beastles的。在www.djbc.net/beastles,MP3链接一个接一个地列在这个页面中。

下列命令将从这个页面中提取出各个链接,把它们写到一个文件中,然后再用wget开始下载这些链接:

$ dog --links http://www.djbc.net/beastles/ | grep
➥mp3 > beastles ; wget -i beastles
--12:58:12--  http://www.djbc.net/beastles/
➥webcontent/djbc-holdittogethernow.mp3
           => 'djbc-holdittogethernow.mp3'
Resolving www.djbc.net... 216.227.209.173
Connecting to www.djbc.net|216.227.209.173|:80...
➥connected.
HTTP request sent, awaiting response... 200 OK
Length: 4,533,083 (4.3M) [audio/mpeg]
100%[=========>] 4,533,083    203.20K/s    ETA 00:00
12:58:39 (166.88 KB/s) - 'djbc-holdittogethernow.
➥mp3' saved [4533083/4533083]

在第5章中学过cat命令,它可以把文件输出到STDOUT(标准输出设备)。5.4节提到了一种比cat更好的命令,叫做dog(确实是真的,因为“狗”比“猫”要好多了)。如果用--links选项,并将它指向某个URL,这时再调用dog,它就会取回那个页面上的所有链接,并显示在STDOUT上。在上面的例子中,用管道符将这些链接发送给grep,让grep过滤掉除了包含mp3以外的所有行,再将生成的MP3链接重定向到一个名为beastles的文本文件(管道和重定向是第4章介绍的内容,grep命令是第9章中介绍的内容)。

分号(在4.1节有所介绍)用于结束前面的命令,并开始引入另一个新命令:wget-i(或--input-file)选项是告诉wget从文件(而不是STDIN,标准输入设备)中查找要下载的URL。如果要下载的链接有多个,可以把它们放在一个文件中,并在wget中使用-i选项。在这个例子中,wget指定通过doggrep而创建的beastles文件,这样就可以依次开始下载每个MP3文件了。

现在,说真的,还能再简单些吗? 嗯,Linux命令行的功能是很强大的!

wget

如果你想备份整个网站,或是下载别人的网站,也得用wget。在上一节用wget抓取回了单个的文件,此外,还可以用它获取整个网站的内容。

说明 请合理使用wget。不要用它下载大型的网站,记住你在复制的是其他人创建和拥有的网站。不要因为想“窃取”什么而刻意复制整个网站。

假设正在浏览www.neato.com这个网站,现在位于www.neato.com/articles/index.htm这个链接上。你想复制/articles目录下的所有东西,而且并不想要这个网站上的其他东西。以下命令就可以完成这个任务:

$ wget -E -r -k -p -w 5 –np
➥http://www.neato.com/articles/index.htm

也可以把各个选项组合起来使用,如下所示:

$ wget -Erkp -w 5 –np
➥http://www.neato.com/articles/index.htm

与上一节一样,该命令以wget作为开始,以想要使用的URL作为结束。命令选项中的-w(或--wait=[#])以前就看到过,它在这里的功能也一样。同样的还有-np(或--no-parent)和-r(或--recursive)选项。接下来看看这个例子中引入的几个新选项。

当下载整个网站时,一些页面可能不是以.htm.html的,而可能以.asp.php.cfm或其他扩展名结尾。这样,如果在你的计算机上查看下载好的网站时,就有问题了。如果你在计算机中运行了某种Web服务器,可能还好说;但很有可能你的计算机中并没有运行任何Apache之类的Web服务器。不过,即便没有Web服务器,用Web浏览器打开以.htm.html结尾的页面,它们也可能在计算机中显示出来。如果使用-E(或--html-extension)选项,wget就会对每个页面进行转换,让它们均以.html结尾,这样就能够在计算机中不用任何特殊的软件而查看它们了。

下载网站可能会引发一些其他问题,不过,幸好可以使用正确的wget选项来避免它们。当在计算机上打开用wget下载的页面时,页面上的链接可能会不起作用,这样就不能在页面之间进行导航。通过指定-k(或--convert-links)选项,就告诉wget重写页面上的链接,以便让它们能够在计算机上起作用。这个选项修复的不仅是指向页面的链接,还会修复指向图片、CSS(Cascading Style Sheet,层叠样式表)等文件的链接。用一下这个选项,你一定会对它感到满意。

说到CSS和图片,有必要说一下为什么需要使用-p(或--page-requisites)选项。为了能够让Web页面正常显示,Web开发人员可能需要指定与页面的HTML一同使用的图片、CSS和JavaScript文件。-p选项会要求wget下载为了正常显示网页而需要的任何文件。有了这个选项,在把页面下载到本机后,打开页面就能看到原来在Web中看到的所有东西。如果没有这个选项,可能最终得到的只是一个不可查看的文件。

wgetman page非常长,介绍得也很详细。如果想了解wget更加复杂的使用方法,它就是最终可以获取信息的地方。如果觉得对wget感兴趣,那么开始读一下这些man page吧,应该会学到很多。

curl

乍看之下,wgetcurl很相像:二者都是用于非交互式地下载文件。不过,除了很多细微区别以外,它们各自都有一个与众不同的显著区别:curl在指定要下载的链接时能够支持URL的序列或集合,而wget则不能这样;同时,wget支持递归下载,而curl则没有这个功能。

说明 这两个程序还有许多其他区别。curl功能的完整列表,参见“Features What Can curl Do”(http://curl.haxx.se/docs/features.html),而有关wget的一些功能则可以参阅“Overview”(www.gnu.org/software/wget/manual/html_node/Overview.html#Overview)。cURL网站有一个对curl和其他类似程序进行比较的图表“Compare cURL Features with Other FTP+HTTP Tools”(http://curl.haxx.se/docs/comparison-table.html),虽然这个图表提供了不少信息,但它明显有些偏向curl(这也不奇怪)。

下面这个例子使用curl下载指定的多个URL序列。National Public Radio(美国国家公共广播)的优秀节目This American Life将它播出的所有节目归档整理,并在其网站上提供Real Audio格式的下载(他们为什么选择Real格式,而不采用一种更开放的格式,确实是个谜)。如果想下载这些Real Audio文件中的10个,只需要使用以下命令:

$ curl -O http://www.wbez.org/ta/[1-10].rm
[1/10]: http://www.wbez.org/ta/1.rm --> 1.rm
--_curl_--http://www.wbez.org/ta/1.rm

注意,上面这个命令是如何使用[1-10].rm来指定想要下载的1.rm2.rm3.rm等文件的。如果WBEZ换了种命名方式,例如,将这些文件命名为one.rmtwo.rmthree.rm,那么应该使用成员集合的方式指定它们,如下所示:

$ curl -O http://www.wbez.org/ta/{one,two,three}.rm

-O(或--remote-name)选项是必需的。如果不使用它,curl会将下载的信息输出到STDOUT,结果终端显示窗口内很快就塞满了一堆没有用的语言符号。-O选项的作用就是让curl把它下载的东西输出到一个文件,并用正在下载的文件名来作为本地文件的名称。

这里介绍了curl的简单用法。虽然它的man page没有wget的那么长,但也提供了很多有用的信息。如果想发挥curl的最大作用,应该阅读一下它们。考虑将这个man page作为必读内容吧。

本章介绍的这些命令都是我个人喜欢使用的一些命令,因为它们可以帮我完成一些非常烦琐、不安全或很有难度的任务。有了这些命令,网络就好像成了连接数据、程序和想象力的通道,这也正是Linux真正的过人之处。通过为用户免费开发出像sshrsyncwgetcurl这样功能强大的工具,Linux倡导的创新和安全性理念可见一斑,不得不用其他各种操作系统的人是不可能体验到这一点的。学习一下ssh,就能安全地连接到其他计算机;精通rsync,就可以安全地实现自动备份;通过wgetcurl指定需要下载的内容,就能尽量有效地取得资源。你将会为自己的付出而感到庆幸!



第16章 Windows联网

Samba是世界上最重要的开源项目之一,因为它让Linux(以及其他基于Unix的机器,如Mac OS X)能够使用SMB(Server Message Block)——所有Microsoft Windows机器上使用的联网协议。有了Samba,Unix机器就能够连接和装载Windows机器上共享的资源,能够将文件打印到与Windows机器相连的共享打印机。Unix机器也可以安装基于Samba的打印机和文件共享,而Windows机器则能够连接和使用它们。事实上,Samba非常有用,以至于不必需要相应的Windows 机器。在Linux机器组成的网络中,可以选择用Samba实现文件和打印机共享,因为它工作得相当出色。

关于如何在服务器上安装Samba,已经有不少好书对此进行了介绍,所以我们不打算介绍相关的管理命令,如smbdsmbcaclssmbpasswd(普通用户可以运行这个命令来修改他们自己的密码,虽然确实如此,不过在实践中差不多都是由Samba服务器管理员来运行这个命令),也不会讨论如何配置smb.conf。而且,本章也假设你已经在Windows、Linux或Mac OS X系统中安装好了SMB共享。而我们关注的重点都集中在客户端:如何找到共享资源的位置,如何连接到它们,以及如何将共享资源装载到你的硬盘中。

提示 下面是几本关于如何安装和管理Samba的不错的书。

  • Sams Samba Unleashed, ISBN: 0672318628,作者:Steve Litt。

  • Sams Teach Yourself Samba in 24 Hours第2版,ISBN:0672322692,作者:Gerald Carter。

  • The Official Samba-3 HOWTO and Reference Guidehttp://samba.org/samba/docs/man/Samba-HOWTO-Collection/)也是非常不错的学习资料。

nmblookup -M [Master Browser]

nmblookup -S [NetBIOS name]

nmblookup -A [IP address]

一个Samba服务器其实使用2个守护进程:smbdnmbdsmbd负责生成共享;nmbd则负责将NetBIOS名称(用于标识使用SMB的机器)映射到其IP地址,这样才可能发现和浏览SMB共享。现在主要介绍与nmbd通信的命令,以便对你正在查询的Windows工作组有总体的了解。

说明 假设在以下例子中使用的是Windows工作组,而不是域。工作组(workgroup)本质上就是由多台机器组成的一个小组,这些机器将自己标识为工作组的成员,表示它们属于某个团体。而域(domain)则需要使用一个中央服务器(在大型网络中可能有多个服务器)来验证想要加入网络的计算机和用户。域就像头体型高大、毛茸茸的野兽,很复杂,如果你想了解有关域的更多详情,最好参考本章介绍中提到的那些图书。

在Windows 工作组中,需要用一台机器来保存跟踪工作组中其他的成员的信息(例如,它们的SMB名称和IP地址)。这台机器就是所谓的Master Browser(主浏览器)。但是工作组中的哪台计算机是Master Browser呢?推选哪台计算机作为Master Browser,得基于它所运行的操作系统。最新的、功能最强的OS总能获胜,比如Windows XP总能打败Windows 2000,而Windows 2000又总能打败Windows 98。

不过,当安装Samba 服务器时,也有可能进行一定的配置,让某个服务器总能在任何这样的选举中胜出,而让其他的机器互相打架;或者干脆进行配置,让服务器总能赢得任何选举。如果已经知道Samba共享的位置,就可以直接连接到Samba共享。不过,万一有任何问题,Samba能够提供你要的Master Browser在什么位置。

要查询网络中的Master Browser,可以运行带有-M(或--master- browser)选项的nmblookup命令,最后再跟一个“-”,这个字符基本上表示“给我找一个Master Browser”。不过,现在的问题是在命令行中不能直接用这个“-”,否则shell会认为它是表示某个选项的开始。所以,还得在它前面先多加一个“--”,这样就告诉shell:接下来的“-”就表示“-”,不是某个选项的组成部分。

$ nmblookup -M -- -
querying __MSBROWSE__ on 192.168.1.255
192.168.1.151 __MSBROWSE__<01>
192.168.1.104 __MSBROWSE__<01>

真不错。在这个例子中,显示在网络中有两个Master Browser,哪一个是实际要使用的呢?因为不同的Master在不同的时间知道的机器也可能不同,这让用户在使用时也是一团糟。一分钟前,用户还能访问某个机器,接着就找不到这个机器在哪了。为什么? 因为只有一台Master知道那台机器在哪,而其他机器都不知道。试着向从事会计工作的Bob解释下这个问题。

要得到有关Master的更多信息,可以使用带有-S(或--status)选项的nmblookup命令,它将返回主机使用的SMB名称,如下所示:

$ nmblookup -S 192.168.1.151
querying 192.168.1.151 on 192.168.1.255
name_query failed to find name 192.168.1.151

可结果并不好用,因为-S选项需要的是NetBIOS名称,而不是IP地址。遗憾的是,现在还不知道机器的NetBIOS名,只知道它的IP地址。其实,这不是问题。只要再加个-A(或--lookup-by-ip)选项,就可以告诉nmblookup现在给出的是IP地址,而不是NetBIOS名称。

$ nmblookup -SA 192.168.1.151
Looking up status of 192.168.1.151
        JANSMAC         <00> -               B <ACTIVE>
        JANSMAC         <03> -               B <ACTIVE>
        JANSMAC         <20> -               B <ACTIVE>
        ..__MSBROWSE__. <01> - <GROUP> B <ACTIVE>
        MILTON          <00> - <GROUP> B <ACTIVE>
        MILTON          <1d> -         B <ACTIVE>
        MILTON          <1e> - <GROUP> B <ACTIVE>

        MAC Address = 00-00-00-00-00-00

现在就能知道IP地址为192.168.1.151的机器将它自己标记为JANSMAC(这一定是一台Mac OS计算机),是MILTON工作组的Master。其他IP地址呢?

$ nmblookup -SA 192.168.1.104
Looking up status of 192.168.1.104
        ELIOT           <00> -         B <ACTIVE>
        ELIOT           <03> -         B <ACTIVE>
        ELIOT           <20> -         B <ACTIVE>
        ..__MSBROWSE__. <01> - <GROUP> B <ACTIVE>
        TURING          <00> - <GROUP> B <ACTIVE>
        TURING          <1d> -         B <ACTIVE>
        TURING          <1e> - <GROUP> B <ACTIVE>

        MAC Address = 00-00-00-00-00-00

IP地址为192.168.1.104的计算机的NetBIOS名称为ELIOT,它是TURING工作组的Master 。所以,这里其实没什么好担心的,因为两台机器分别是不同工作组的Master。将自己标记为MILTON组成员的机器会向JANSMAC查询信息,而属于TURING组的机器使用的是ELIOT

说明 有关这些例子中输出内容的更多信息,可以参见Microsoft的Knowledge Base(http://support.microsoft.com/kb/q163409/)。

nmblookup -T

nmblookup是找到正在通过Samba共享文件和打印机的任何计算机的快速方法。在该命令后面添加-T选项,后面再跟上"*"(对,这里必须使用双引号来告诉shell:不能将星号*作为代表当前工作目录中所有文件的通配符)。

$ nmblookup -T "*"
querying * on 192.168.1.255
192.168.1.151 *<00>
192.168.1.104 *<00>
192.168.1.10 *<00>

结果显示这个LAN(局域网)中有三台计算机有Samba共享。要找到这些机器各自的Master Browser,可以看看上一节介绍的命令;要得到每台计算机提供的共享列表,请继续阅读下一节。

smbclient

使用前一节介绍的命令,现在就能知道了哪台计算机上有Samba共享,但还想知道它共享了哪些内容,该怎么办呢?smbclient命令是个多用途的工具,可以用于连接和使用共享资源。不过,从更底层的应用来看,它也可以列出计算机上可用的共享。只要使用-L(或--list)选项,后面跟上NetBIOS名称,或IP地址。在提示输入密码时,直接按Enter键,如下所示:

$ smbclient -L ELIOT
Password:
Anonymous login successful
Domain=[TURING] OS=[Unix] Server=[Samba 3.0.14a-Ubuntu]
Sharename Type Comment

print$    Disk Printer Drivers
documents Disk Shared presentations and other files
IPC$       IPC  IPC Service (eliot server (Samba,   Ubuntu))
ADMIN$     IPC  IPC Service (eliot server (Samba,   Ubuntu))
Anonymous login successful
Domain=[TURING] OS=[Unix] Server=[Samba 3.0.14a-Ubuntu]

在这个例子中,既可以看到所有匿名登录可以使用的共享,也可以看到在服务器的smb.conf文件中标明可以浏览的所有文件。如果要查看登录用户可以使用的共享,需要添加-U(或--user)选项,后面跟上在Samba服务器上的Samba用户名。

说明 Samba用户名与Samba服务器上Linux(或Windows和Mac OS X)的用户名可能相同,也可能不相同。需要查看Samba服务器才可以确认这些信息。

$ smbclient -L ELIOT -U scott
Password:
Domain=[ELIOT] OS=[Unix] Server=[Samba 3.0.14a-Ubuntu]
Sharename Type Comment

print$    Disk Printer Drivers
documents Disk Shared presentations and other files
IPC$       IPC  IPC Service (eliot server (Samba,   Ubuntu))
ADMIN$     IPC  IPC Service (eliot server (Samba,   Ubuntu))
scott     Disk Home Directories
Domain=[ELIOT] OS=[Unix] Server=[Samba 3.0.14a-Ubuntu]

在登录以后,看到一个新的共享,即这个用户的主目录scott。也就是说,登录之后就可以在实际使用的共享目录中找到东西,这将是下一节要学习的内容。

提示 如果想测试刚才在Samba服务器上创建的共享,一个不错的方法就是在那台机器上打开shell,并输入以下命令:

  $ smbclient -L localhost

在提示输入密码时,直接按Enter键。用这种方法,你就可以快速查看新增加的共享是否可用。当然,如果事先没有将这个共享的浏览权限对所有人开放,就需要用-U选项,让能够查看该共享的用户登录。

smbclient

在知道Samba服务器上有可供使用的共享资源之后,就能够登录和使用它们了。还是使用smbclient命令,命令格式如下所示:

smbclient //server/share -U username

现在要登录的不是服务器,而是服务器上的共享资源。要访问有密码保护的资源(非常建议为共享增加密码保护),需要先指定一个用户。例如,要访问服务器ELIOT上的documents共享,可以使用以下命令:

$ smbclient //eliot/documents -U scott
Password:
Domain=[ELIOT] OS=[Unix] Server=[Samba 3.0.14a-Ubuntu]
smb: \>

如果能够出现Samba提示符(smb: \>),就表明操作成功了。注意,操作中提示输入密码。假设用户scott的密码是123456(这是个非常糟糕的密码,不过,只是假想的而已),可以输入以下命令,操作过程中就不会再提示密码了:

$ smbclient //eliot/documents -U scott%123456

然而,这一做法是非常糟糕的,因为任何人只要看了.bash_history文件(在11.1节中介绍)或使用ps命令查看正在运行的进程列表(参见12.1节),就能够看到密码。绝对不要直接在命令中附加密码内容,在提示输入密码时再输入,这是一种更为安全的实践。

提示 如果正在编写一段脚本,即使不能通过交互方式来登录,也不能将密码直接附加到用户名上。应该使用-A(或—authen- tication-file=[filename])选项,通过该选项来引用一定的凭证文件(credentials file)。使用scott用户,这个凭证文件应该包含以下内容:

  username = scott
  password = 123456

一定要使用chmod命令(参见第7章)为这个文件设置一定的权限,不要让所有人都可以读取它。

在连接到Samba 共享以后,就可以使用很多类似于在命令行使用FTP时用到的命令(参见表16-1)。

表16-1 smbclient的重要命令
命  令 含  义
cd切换目录
exit关闭与Samba服务器的连接
get将指定的文件复制到本机
help获取与命令相关的帮助
lcd将目录切换到本机
ls列出Samba服务器上当前工作目录中的文件
mget将匹配一定模式的所有文件复制到本机
mkdir在Samba服务器上创建一个新目录
mput将匹配一定模式的所有文件复制到Samba服务器
put将指定的文件复制到Samba服务器
rm将指定的文件从Samba服务器删除

除了表16-1包含的基本信息外,在登录到Samba服务器后,只需输入help,就可以查看其他命令的帮助信息。完成操作以后,只要输入exit,就可以退出服务器的登录,返回到自己的计算机。

smbmount

smbumount

使用smbclient访问Samba共享,真太好了。不过,与使用GUI相比,或者想在共享资源上做很多事情,使用命令行界面还是很痛苦的体验。这时,应该使用smbmount,它会将Samba共享挂载到本地文件系统中,好像它就是硬盘驱动器上的一部分。在挂载好以后,你就可以用自己喜欢的文件管理器来方便地访问共享中的文件,甚至还能够打开像文字处理器之类的程序,不必再为直接打开多个程序而忙得团团转。

为了在文件系统中挂载Samba,必须先创建一个载入点(mount point),以便Linux可以载入共享。例如,如果想挂载来自ELIOTdocuments共享,可能需要创建如下所示的目录:

$ mkdir /home/scott/eliot_documents

现在就可以运行smbmount命令,同时也提供了很多完整的选项。

$ smbmount //eliot/documents /home/scott/eliot_
➥documents -o credentials=/home/scott/bin
➥/credentials_eliot,fmask=644,dmask=755,uid=1001,
➥gid=1001,workgroup=TURING
smbmnt must be installed suid root for direct user
➥mounts (1000,1000)
smbmnt failed: 1

在介绍这些选项之前,先来看看上面的smbmount命令为什么会失败。第二行提示了失败的原因:smbmntsmbmount调用的一个命令)只能由root用户运行。如果非root用户挂载Samba共享,就会出现问题了。权宜之计就是为smbmnt设置suid root(参见7.10节中对suid的介绍)。

说明 你正在赋予普通用户能够使用smbmnt的权力,好像他们是root用户一样。不过,这并不是什么大问题。与告诉他们root的密码,或是每次他们想挂载Samba共享时都要你亲临现场相比,这样的效果要好多了。

以下是如何为smbmnt设置suid root的方法。

说明 为了节省篇幅,这里删除了一些使用ls-l命令时通常会看到的详细信息。

# ls /usr/bin/smbmnt
-rwxr-xr-x  1 root root /usr/bin/smbmnt
# chmod u+s /usr/bin/smbmnt
# ls -l /usr/bin/smbmnt
-rwsr-xr-x  1 root root /usr/bin/smbmnt

现在再试一下smbmount命令。

$ smbmount //eliot/documents /home/scott/eliot_
➥documents -o credentials=/home/scott
➥/credentials_eliot,fmask=644,dmask=755,uid=1001,
➥gid=1001,workgroup=TURING
$ ls -F /home/scott/eliot_documents
presentations/  to_print/

接下来浏览一下刚才使用的命令。在smbmount之后,//eliot/documents指定了准备连接的Samba服务器和Samba共享。接下来的参数是载入点的路径/home/scott/eliot_documents-o表示接下来还要指定选项。

除了使用credentials=/home/scott/credentials_eliot,还能够使用以下任何参数形式:

  • username=scott,password=123456
  • username=scott%123456
  • username=scott

第一种和第二种方法一点也不安全,因为可以在.bash_historyps中看到这里输入的密码信息。千万不要这么做!最后一种选择将在运行过程中提示输入密码,这是一种安全的方法,因为密码信息不会在.bash_historyps中出现。但是,如果想让挂载Samba共享的处理自动进行,那最后这种选择也不行。

还得使用credentials=/home/scott/credentials_eliot。这是在告诉smbmount,用户名和密码保存在一个文件中,其格式为:

username = scott
password = 123456

这与16.4节中介绍的凭证文件类似,在创建好凭证文件以后,要使用chmod命令来严格控制谁可以查看这个文件。如果你不在意必须自动登录处理,那就务必使用username=scott选项,提示用户输入密码,这肯定是种安全的方式。

fmaskdmask选项分别控制在挂载后的Samba共享中创建的新文件和目录的默认权限。644为文件产生rw-r--r--的权限,而755最终将为目录产生rwxr-xr-x的权限。

说明 没有忘记前面介绍的内容吧?再看一下7.6节。

实际上,所有用户名和组名称都有一个对应的数字,分别在/etc/passwd/etc/group这两个文件中可以看到这些信息。在当前的计算机上,你的用户ID可能是1000;但是在Samba服务器上,1000可能代表另一个完全不同的用户。组ID也存在同样的问题。所以在命令中用uid=1001gid=1001就可以告诉Samba服务器你是Samba服务器上的哪个用户。换句话说,需要查看一下Samba服务器(不是你的本机)上/etc/passwd/etc/group这两个文件中的这些ID编号。否则,即便你可能创建了文件和目录,结果发现还是不能真正拥有它们,也不能像你希望的那样来随意使用这些文件和目录。

最后,workgroup=TURING指定工作组,其作用一目了然。

在挂载好共享以后,就能够打开文件管理器,开始浏览eliot_ documents的内容;也能够启动OpenOffice.org,直接打开eliot_ documents/presentations/中的文件;或者打开eliot_documents/to_print中的某个PDF文件,直接将其发送到打印机。将以下几行代码放到计算机的/etc/fstab文件中,就可以自动挂载共享。

//eliot/documents /home/scott/eliot_documents smbfs
➥credentials=/home/scott/credentials_eliot,fmask=644,
➥dmask=755,uid=1001,gid=1001,workgroup=TURING 0 0

警告 修改/etc/fstab文件时要非常小心,因为任何错误都将可能造成系统无法启动。虽然有办法能修复这个问题(参考Hacking Knoppix一书中的一些提示建议),但这也是件麻烦事,所以最好小心谨慎,避免出错。有关fstab文件的更多信息,可以在系统上查看man fstab

如果以后不需要再访问eliot_documents上的Samba共享,可以使用smbumount命令(注意,这个命令是umount,而不是unmount)卸载(unmount)。

$ smbumount eliot_documents
smbumount must be installed suid root

真烦人!还要为smbumount设置suid root。

$ ls -l /usr/bin/smbumount
-rwxr-xr-x  1 root root /usr/bin/smbumount
$ sudo chmod u+s /usr/bin/smbumount
$ ls -l /usr/bin/smbumount
-rwsr-xr-x  1 root root /usr/bin/smbumount

只有挂载eliot_documents的用户才能够卸载eliot_documents。当然,root用户也能够卸载,因为root可以做任何他想做的事。

$ smbumount eliot_documents

就这么简单。如果想验证smbumount是否成功执行,试一下ls eliot_documents。如果成功,这个目录下将没有任何东西,表明已经从Samba共享断开了。

很多Linux用户都愿意假装微软不存在,但实际上又不得不承认还生活在Windows的世界中。Samba 让这些不同系统共存变得相当简单,它让Linux用户可以访问Windows机器上的资源,也让其他操作系统的用户可以访问Linux机器上的资源。微软可能不很喜欢Samba,但Samba确实是一种成熟、稳定、功能强大的技术,而且由于Samba是开源的,因此它非常有助于将全世界的计算机更紧密地连接在一起。无论是在家庭,还是在商业领域,Samba都有助于减少对Windows服务器的需求,用某位女前辈的话来说,Samba真是个好东西!



没有精选讨论主题。

全部讨论主题 查看精选主题

没有讨论主题。
😃 😅 😆 😁 😂 😊 😄 😠 😩 😲 😞 😵 😰 😒 😍 😤 😜 😝 😋 😘 😚 😷 😳 😢 😭 😨 😣 😡 😌 😖 😔 😱 😪 😏 😓 😥 😫 😉 👊 👍 👆 👇 👈 👉 👋 👏 👌 👎 👐 💓 💔 💕 💖 💗 💘 💙 💚 💛 💜 💝 💞 💟