第 1 章 GNU/Linux 教程

我认为学习一个计算机系统,就像学习一门新的外语。虽然教程和文档是有帮助的,但你必须自己练习。为了帮助你平滑起步,我详细说明一些基本要点。

Debian GNU/Linux中最强大的设计来自Unix操作系统,一个多用户多任务的操作系统。你必须学会利用这些特性以及Unix和GNU/Linux的相似性。

别回避面向 Unix 的文档,不要只是依赖于 GNU/Linux 文档,这样做会剥夺你了解许多有用的信息。

[注意]注意

如果你在任何类 Unix 系统中使用过一段时间的命令行工具,你可能已经掌握了这份文档中的内容。那请把它当做一个实战检验和回顾。

1.1. 控制台基础

1.1.1. shell 提示符

启动系统之后,如果你没有安装 X 窗口系统和显示管理器(例如gdm3),那么你就会看对字符登录界面。假设你的主机名为foo,那么登录提示符将如下所示。

  1. foo login:

如果你安装了一个 GUI 环境,例如 GNOMEKDE,那么你能够用 Ctrl-Alt-F1进入登录提示符,同时你可以通过Alt-F7回到GUI环境(更多详情请参阅下文第 1.1.6 节 “虚拟控制台”)。

在登录提示符下,你输入你的用户名,例如penguin,然后按回车键,接下来输入你的密码并再次按回车键。

[注意]注意

遵循Unix传统,Debian系统下的用户名和密码是大小写敏感的。用户名通常由小写字母组成。第一个用户账号通常在安装期间进行创建。额外的用户账号由root用户用 adduser(8)创建。

系统以保存在 “/etc/motd“ 中的欢迎信息(Message Of The Day)来开始,同时显示一个命令提示符。

  1. Debian GNU/Linux jessie/sid foo tty1
  2. foo login: penguin
  3. Password:
  4. Last login: Mon Sep 23 19:36:44 JST 2013 on tty3
  5. Linux snoopy 3.11-1-amd64 #1 SMP Debian 3.11.6-2 (2013-11-01) x86_64
  6. The programs included with the Debian GNU/Linux system are free software;
  7. the exact distribution terms for each program are described in the
  8. individual files in /usr/share/doc/*/copyright.
  9. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
  10. permitted by applicable law.
  11. foo:~$

现在,你就在 shell) 下。shell 解析你的命令。

1.1.2. X 下的 shell 提示符

如果你安装了带有显示管理器的 X Window System,例如通过在安装 Debian 时选择 “桌面环境” 所安装GNOMEgdm3,那么你在启动系统后将使用图形登录界面。输入你的用户名和密码可以登录到非特权用户帐号。使用 Tab 键(跳格键)可以在用户名和密码之间移动,也可以使用鼠标左击。

要在X窗口下获得shell提示符,你必须启动一个x终端模拟器程序,例如gnome-terminal(1)、rxvt(1)或xterm(1)。在GNOME桌面环境下,你可以点击“应用程序”→“附件”→“终端”来打开终端。

你还可以看下下面的 第 1.1.6 节 “虚拟控制台” 章节。

在其它一些桌面系统(如 fluxbox)下面,可能没有明显的开始菜单入口。如果是这种情况,试下右击桌面屏幕并希望能有弹出菜单。

1.1.3. root 账户

root 账户也被称作超级用户或特权用户。用这个账户,你能够履行下面的系统管理任务。

  • 读、写和删除系统上的任何文件,不顾它们的文件权限

  • 设置系统上任何文件的所有者和权限

  • 设置系统上任何非特权用户的密码

  • 免用户密码登录任何帐户

无限权力的 root 账户,要求你慎重和负责任的使用。

[警告]警告

千万不要和其他人共享 root 密码.

[注意]注意

一个文件(包括硬件设备,如CD-ROM等,这些对Debian系统来说都只是一个文件)的权限可能会导致非root用户无法使用或访问它 。虽然在这种情况下,使用root帐户是一个快速的方法,但正确的解决方法应该是对文件权限和用户组的成员进行合适的设置(参见第 1.2.3 节 “文件系统权限”)。

1.1.4. root shell 提示符

这里有一些基本的方法可以让你在输入root密码后获得root的shell提示符。

  • 在字符界面的登录提示符,键入 root 作为用户名登录。

  • 在 GNOME 桌面环境下点击 “应用程序” → “附件” → “Root 终端”。

  • 在任意用户的shell提示符下输入“su -l”。

    • 这不会保存当前用户的环境设定。
  • 在任意用户的shell提示符下输入“su”。

    • 这会保存当前用户的一些环境设定。

1.1.5. GUI 系统管理工具

如果你的桌面菜单没有适当的权限启动系统管理工具,你可以在 X 终端模拟器(例如gnome-terminal(1)、rxvt(1)或xterm(1))中 root 的 shell 提示符下启动它。参见第 1.1.4 节 “root shell 提示符”第 7.8.5 节 “以 root 运行 X 客户端”

[警告]警告

永远不要在显示管理器(例如gdm3(1))的提示符下输入root来使用 root 账户启动 X 显示/会话 管理器。

[警告]警告

永远不要在显示关键信息的 X Window下运行不受信任的远程 GUI 程序,因为它可能会监听你的 X 屏幕。

1.1.6. 虚拟控制台

在默认的 Debian 系统中,有6个可切换的类VT100字符控制台,可以直接在 Linux 主机上启动 shell。除非你处于 GUI 环境下,否则你可以同时按下左 Alt 键F1F6之一的键在虚拟控制台间切换。每一个字符控制台都允许独立登陆账户并提供多用户环境。这个多用户环境是伟大的 Unix 的特性,很容易上瘾。

如果你处于 X Window 系统中,你可以通过Ctrl-Alt-F1键前往字符控制台1,也就是同时按下左 Ctrl 键左 Alt 键F1 键。你可以按下Alt-F7回到 X Window System,它一般运行在虚拟控制台7。

你也可以使用命令行切换到另一个虚拟控制台,例如切换到控制台1。

  1. # chvt 1

1.1.7. 怎样退出命令行提示符

在命令行输入Ctrl-D,即同时按下左侧-Ctrl-键d-键,即可关闭 shell 活动。如果你正处于字符控制台,你将会返回到登录提示行。尽管这些控制字符 “control D” 使用了大写字母,你并不需要按住 Shift-键。Ctrl-D 也可以简写为 ^D。或者,你也可以键入 ”exit” 退出命令行。

如果你位于x 终端模拟器(1)中,你可以使用这个关闭x 终端模拟器窗口。

1.1.8. 怎样关闭系统

就像任何其他的现代操作系统一样,Debian 会通过内存中的缓存数据进行文件操作以提高性能,因此在电源被安全地关闭前需要适当的关机过程,通过将内存中的数据强制写入硬盘来维持文件的完整性。如果软件的电源控制可用,那么关机过程中会自动关闭系统电源。(否则,你可能需要在关机过程之后按电源键几秒钟。)

在普通多用户模式模式下,可以使用命令行关闭系统。

  1. # shutdown -h now

在单用户模式下,可以使用命令行关闭系统。

  1. # poweroff -i -f

另外,如果在“/etc/inittab”中含有“ca:12345:ctrlaltdel:/sbin/shutdown -t1 -a -h now”,那么你可以按下Ctrl-Alt-Delete(同时按下左 Ctrl 键左 Alt 键Delete)来关机。参见inittab(5)获取更多细节。

参见第 6.9.6 节 “怎样通过 SSH 关闭远程系统”

1.1.9. 恢复一个正常的控制台

当做了一些滑稽的事(例如“cat<二进制文件>”)后,屏幕会发狂,你可以在命令行输入“reset”。你可能无法在屏幕上看到你输入的命令。你也可以输入“clear”来清屏。

1.1.10. 建议新手安装的额外软件包

尽管连无需任何桌面环境的 Debian 系统最小安装都提供了基本的 Unix 功能,但对新手而言,使用 apt-get(8) 安装一些基于字符终端的命令行和 curses 软件包(例如 mcvim)依旧是一个不错的主意。

  1. # apt-get update
  2. ...
  3. # apt-get install mc vim sudo
  4. ...

如果你已经安装了这些软件包,那么不会有新的软件包被安装。

表 1.1. 有趣的文本模式程序包列表

软件包流行度大小说明
mcV:59, I:2361482文本模式的全屏文件管理器
sudoV:563, I:8064555给普通用户授予部分 root 权限的程序
vimV:106, I:3983231Unix 文本编辑器 Vi 的改进版,一个程序员的文本编辑器(标准版)
vim-tinyV:62, I:9701553Unix 文本编辑器 Vi 的改进版,一个程序员的文本编辑器(精简版)
emacs-noxV:4, I:1718364GNU 项目的 Emacs,基于 Lisp 的扩展文本编辑器
w3mV:31, I:2842289文本模式的万维网浏览器
gpmV:11, I:17530Unix 风格的文本控制台复制粘贴工具(守护进程)

您也可以考虑阅读一些其他的信息文档。

表 1.2. 软件包信息文档列表

软件包流行度大小说明
doc-debianI:854166Debian 项目文档,(Debian 常见问题)和其它文档
debian-policyI:364306Debian 策略手册和相关文档
developers-referenceI:61917Debian 开发者指导方针和信息
maint-guideI:4987Debian 新维护者手册
debian-historyI:14285Debian 项目历史
debian-faqI:849817Debian 常见问题

你可以用下面的命令安装这些包。

  1. # apt-get install package_name

1.1.11. 额外用户账号

如果你不想用你自己的主用户账户来进行下面的练习操作,你可以使用下面的方式创建一个练习用户账户,比如说,创建一个用户名为 fish 的账号。

  1. # adduser fish

回答所有问题。

这将创建一个名为 fish 的新账号。在你练习完成后,你可以使用下面的命令删除这个用户账号和它的用户主目录。

  1. # deluser --remove-home fish

1.1.12. sudo 配置

对于典型的单用户工作站,例如运行在笔记本电脑上的桌面Debian系统,通常简单地配置sudo(8)来使为非特权用户(例如用户penguin)只需输入用户密码而非root密码就能获得管理员权限。

  1. # echo "penguin ALL=(ALL) ALL" >> /etc/sudoers

另外,可以使用下列命令使非特权用户(例如用户penguin)无需密码就获得管理员权限。

  1. # echo "penguin ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

这些技巧只对你管理的单用户工作站中那个唯一的用户有用。

[警告]警告

在多用户工作站中不要建立这样的普通用户账户,因为它会导致非常严重的系统安全问题。

[小心]小心

在上述例子中,用户penguin的密码及账号要有和root账号密码同样多的保护。

[小心]小心

在这种情况下,管理员权限被赋予那些有权对工作站进行系统管理任务的人。永远不要让你的公司行政管理部门或你的老板进行管理(例如给予他们权限),除非他们获得了授权并有这样的能力。

[注意]注意

为了对受限的设备和文件提供访问权限,你应该考虑使用来提供受限访问,而不是通过sudo(8)来使用 root 权限。

[注意]注意

随着越来越细致周密的配置,sudo(8)可以授予一个共享系统上的其它用户有限的管理权限而不共享root密码。这可以帮助对有多个管理员的主机进行责任追究,你可以了解到是谁做什么。另一方面,你可能不想任何人有这样的权限。

1.1.13. 动手时间

现在你已经准备好在 Debian 系统上开工了,只要你使用非特权用户账号就不会有风险。

这是因为 Debian 系统(即使是默认安装)会设置适当的文件权限来防止非特权用户对系统造成破坏。当然,可能仍然有一些漏洞可以利用,但关心这些问题的人不应该阅读这一节,而应该去阅读 Debian 安全手册

我们使用下面的方式,把 Debian 系统当作一个 类 Unix 系统来学习。

1.2. 类 Unix 文件系统

在GNU/Linux和其他类Unix操作系统中,文件被组织到目录)中。所有的文件和目录排放在以“/”为根的巨大的树里。叫它树是因为如果你画出文件系统,它看起来就像一棵树,但是它是颠倒过来的。

这些文件和目录可以分散在多个设备中。mount(8)用于把某个设备上找到的文件系统附着到巨大的文件树上。相反的,umount(8)把它再次分离。在最近的 Linux 内核里,mount(8)带某些参数时可以把文件树的一部分绑定到另外的地方,或者可以把文件系统挂载为共享的、私有的、从设备、或不可绑定的。对每个文件系统支持的挂载选项可以在/usr/share/doc/linux-doc-*/Documentation/filesystems/找到。

Unix系统上叫做目录,某些其他系统上叫做文件夹。请同样留意,在任何Unix系统上,没有的驱动器的概念,例如“A:”。这只有一个文件系统,并且所有东西都包含在内。这相对于Windows来说是一个巨大的优点。

1.2.1. Unix 文件基础

下面是一些 Unix 文件基础。

  • 文件名是 区分大小写 的。也就是说, “MYFILE“ 和 “MyFile“ 是不同的文件。

  • 根目录意味着文件系统的根,简单的称为“/”,不要把它跟root用户的家目录“/root”混淆了。

  • 每个目录都有一个名字,它可以包含任意字母或除了/”以外的符号。根目录是个特例。它的名字是“/”(称作“斜线”或“根目录”),并且它不能被重命名。

  • 每个文件或目录都被指定一个全限定文件名绝对文件名,或路径,按顺序给出必须经过的目录从而到达相应目录。这三个术语是同义的。

  • 所有的全限定文件名以“/”目录开始,并且在每个目录或文件名之间有一个“/”。第一个“/”是最顶层目录,其他的“/”用于分隔跟着的子目录。直到到达最后的入口,即实际文件的名称。这些话可能会令人困惑。用下面这个全限定文件名作为例子:“/usr/share/keytables/us.map.gz”。不过,人们也把它的基名“us.map.gz”单独作为文件名。

  • 根目录有很多分支,例如“/etc/”和“/usr/”。这些子目录依次分出更多的子目录,例如“/etc/init.d/”和“/usr/local/”。这整体叫做“目录树”。你可以把一个绝对文件名想象成从“/”这棵树的基到某个分支(一个文件)的结尾的一条路径。你也听到人们谈论目录树,就好像它是一个包含所有直系后代的“家庭”树的一个图,这个图叫做根目录(“/”):因此子目录有目录,并且一条路径显示了一个文件完整的祖先。也有相对路径从其他地方开始,而不是从根目录。 你应该还记得目录“../”指向父目录。这个术语也适用于其他类似目录的结构,如分层数据结构。

[注意]注意

虽然你可以在文件名中使用任意的字幕或者符号, 但是在实际情况下这样做是一个坏主意. 最好避免使用一些在命令行里面含有特殊意义的字符, 比如空格, 制表符, 换行符, 和其它的特殊字符: { } ( ) [ ] ‘ ` “ \ / >< | ; ! #&^ * % @ $. 如果你想有一个区分度良好的命名, 比较好的选择是利用 时期, 连字符和下划线. 你也可以每个单词的首字母大写, 这叫大驼峰命名法, 比如这样 “LikeThis“. 经验丰富的Linux用户会趋向于在文件名中不使用空格.

[注意]注意

这个 “root” 可能既表示 “超级用户root” 又表示 “ 根目录”(/root) . 应该根据上下文确定它的用法.

[注意]注意

单词path不仅表示包含全限定文件名, 也可能表示命令搜索的路径. 通常路径真实的意思是需要通过上下文来明确.

关于文件层次的最佳详细实践在文件系统层次标准(“/usr/share/doc/debian-policy/fhs/fhs-2.3.txt.gz“ 和 hier (7)). 你应该记住以下的一些标准作为开始学习的步骤.

表 1.3. 重要目录的用途列表

目录目录用途
/根目录
/etc/系统范围的配置文件
/var/log/系统日志文件
/home/所有非特权用户的用户目录

1.2.2. 文件系统深入解析

按照UNIX系统的传统,Debian GNU / Linux 的文件系统是在物理数据存储设备诸如磁盘或其他存储设备上,与硬件设备的交互,如控制台和远程串口终端都是以统一的方式呈现在 “/ dev /” 下面。

每个文件、目录、命名管道(一种两个程序间共享数据的方法)或 Debian GNU/Linux 系统上的物理设备都有一个叫做 inode的数据结构,描述了其相关属性,例如拥有它的用户(所有者),它属于的组,最后一次访问时间,等等。把所有东西都表示在文件系统中的想法是来源于 Unix,现代的 Linux 内核则将这个思路进行了扩展。现在,甚至有关计算机上正在运行的进程的信息都可以在文件系统中找到。

这个对物理实体和内部进程的统一和抽象是非常强大的,因为这允许我们用同样的命令对许多完全不同的设备进行同样的操作。甚至可以通过向链接到运行进程的特殊文件写入数据来改变内核的运行方式。

[提示]提示

如果您需要识别文件树和物理实体之间的对应关系,请尝试不带参数运行mount(8)。

1.2.3. 文件系统权限

类Unix系统的文件系统权限被定义给三类受影响的用户。

  • 拥有这个文件的用户u

  • 这个文件所属的其他用户(g

  • 所有其余的用户(o),同样称为“世界”和“所有人”

对文件来说,每个对应权限允许下列动作。

  • 可读r)权限允许所有者检查文件的内容。

  • 可写w)权限允许所有者修改文件内容。

  • 可执行x)权限允许所有者把文件当做一个命令运行。

对于目录来说,每个对应权限允许下列动作。

  • 可读r)权限允许所有者列出目录内的内容。

  • 可写w)权限允许所有者添加或删除目录里面的文件。

  • 可执行x)权限允许所有者访问目录里的文件。

在这里,一个目录的可执行权限意味着不仅允许读目录里的文件,还允许显示他们的属性,例如大小和修改时间。

ls(1)用于显示文件和目录的权限信息(更多)。当运行时带有“-l”选项,它将按给定顺序显示下列信息。

  • 文件类型(第一个字母)

  • 文件的访问权限(9个字符,三个字符组成一组按照用户、组、其他的顺序表示)

  • 链接到文件的硬链接数

  • 文件所有者的用户

  • 这个文件所属的

  • 以字符(字节)为单位的文件大小

  • 文件的日期和时间(mtime)

  • 文件的名字

表 1.4. “ls -l”输出的第一个字符列表

字符说明
-普通文件
d目录
l符号链接
c字符设备节点
b块设备节点
p命名管道
s套接字

chown(1)用于 root 账户修改文件的所有者。chgrp(1)用于文件的所有者或 root 账户修改文件所属的组。chmod(1)用于文件的所有者或root账户修改文件和文件夹的访问权限。操作一个foo文件的基本语法如下 。

  1. # chown <newowner> foo
  2. # chgrp <newgroup> foo
  3. # chmod [ugoa][+-=][rwxXst][,...] foo

例如,你可以按照下面使一个目录树被用户foo所有,并共享给组bar

  1. # cd /some/location/
  2. # chown -R foo:bar .
  3. # chmod -R ug+rwX,o=rX .

有三个更加特殊的权限位。

  • Set-User-ID(SUID)位(sS替换用户的x

  • Set-Group-ID(SGID)位(sS替换组的x

  • 粘滞位(tT替代其他用户的x

如果“ls -l”对这些位的输出是大写字母,则表示这些输出下面的执行位未设置

给一个可执行文件设置 Set-User-ID 位将允许一个用户以他自己的ID运行这个可执行文件(例如 root 用户)。类似的,给一个可执行文件设置了Set-Group-ID 位将允许一个用户以文件所属组的 ID 运行该文件。(例如 root 组)。由于这些设置可能导致安全风险,设置它们为可用的时候需要格外留意。

在一个目录上设置“Set-Group-ID”将打开类 BSD 的文件创建计划,所有在目录里面创建的文件将属于目录所属的

给一个目录设置“粘滞位”将保护该目录内的文件不被其所有者之外的一个用户删除。为了保护一个在像“/tmp”这样所有人可写或同组可写的目录下文件内容的安全,不仅要去除可写权限,还要给其所在目录设置粘滞位。否则,该文件可以被任意对其所在目录有写权限的用户删除并创建一个同名的新文件。

这里有一点有趣的文件权限例子。

  1. $ ls -l /etc/passwd /etc/shadow /dev/ppp /usr/sbin/exim4
  2. crw------T 1 root root 108, 0 Oct 16 20:57 /dev/ppp
  3. -rw-r--r-- 1 root root 2761 Aug 30 10:38 /etc/passwd
  4. -rw-r----- 1 root shadow 1695 Aug 30 10:38 /etc/shadow
  5. -rwsr-xr-x 1 root root 973824 Sep 23 20:04 /usr/sbin/exim4
  6. $ ls -ld /tmp /var/tmp /usr/local /var/mail /usr/src
  7. drwxrwxrwt 14 root root 20480 Oct 16 21:25 /tmp
  8. drwxrwsr-x 10 root staff 4096 Sep 29 22:50 /usr/local
  9. drwxr-xr-x 10 root root 4096 Oct 11 00:28 /usr/src
  10. drwxrwsr-x 2 root mail 4096 Oct 15 21:40 /var/mail
  11. drwxrwxrwt 3 root root 4096 Oct 16 21:20 /var/tmp

chmod(1)有另一种数值模式来描述文件权限。这种数字模式使用3到4位八进制(底为8)数。

表 1.5. chmod(1) 命令文件权限的数字模式

数字说明
第一个可选数字Set-User-ID (=4), Set-Group-ID (=2) 和 粘滞位 (=1) 之和
第二个数字用户可读 (=4), 可写 (=2)和 可执行 (=1) 权限之和
第三个数字权限同上
第四个数字位其他用户权限同上

这听起来很复杂实际上相当简单。如果你把“ls -l”命令输出的前几列(2-10),看成以二进制(底为2)表示文件的权限(“-”看成0,“rwx”看成1),你应该可以理解用数字模式值的最后3位数字对文件权限的八进制表示。

尝试下列例子

  1. $ touch foo bar
  2. $ chmod u=rw,go=r foo
  3. $ chmod 644 bar
  4. $ ls -l foo bar
  5. -rw-r--r-- 1 penguin penguin 0 Oct 16 21:39 bar
  6. -rw-r--r-- 1 penguin penguin 0 Oct 16 21:35 foo
[提示]提示

如果你需要在 shell 脚本中访问“ls -l”显示的信息,你需要使用相关命令,如test(1),stat(1)和readlink(1)。shell 内置命令,如“[”或“test”,可能也会用到。

1.2.4. 控制新建文件的权限:umask

什么权限将应用到新建文件受 shell 内置命令 umask 的限制。参见dash(1),bash(1),和内建命令(7)。

  1. (文件权限) = (请求的文件权限) & ~(umask 值)

表 1.6. umask值举例

umask值创建的文件权限创建的目录权限用法
0022-rw-r—r—-rwxr-xr-x仅所属用户可写
0002-rw-rw-r—-rwxrwxr-x仅所属组可写

Debian 默认使用用户私人组(UPG)。每当一个新用户添加到系统的时候都会创建一个UPG。UPG 的名字和创建它的用户相同,这个用户是这个UPG的唯一成员。自从每个用户都有自己的私人组之后,把umask设置成0002变得更安全了。(在某些 Unix 变体中,把所有普通用户设置到一个叫users的组是非常常见的做法,在这种情况下,出于安全考虑把umask设为0022是一个好主意)

[提示]提示

通过把 “umask 002” 写入 ~/.bashrc 文件打开UPG。

1.2.5. 一组用户的权限(组)

为了使组权限应用到一个特定用户,这个用户需要通过使用 “sudo vigr” 编辑 /etc/group 以及使用 “sudo vigr -s” 编辑 /etc/gshadow 成为该组的成员。你需要注销之后重新登录(或运行 “exec newgrp”)以启用新的组配置。

[注意]注意

或者,你可以通过添加一行 “auth optional pam_group.so”到 “/etc/pam.d/common-auth” 以及配置 “/etc/security/group.conf” ,使得在身份验证过程动态添加用户到组。(参见第 4 章 认证。)

在 Debian 系统中,硬件设备是另一种文件。如果你从一个用户账户访问某些设备出现问题,例如CD-ROM和USB记忆棒,你需要使这个用户成为相关组的成员。

一些著名的由系统提供的组允许其成员不需要 root 权限访问某些特定的文件和设备。

表 1.7. 关于文件访问的由系统提供的著名组列表

可访问文件和设备的描述
dialout完全及直接的访问串口端口(“/dev/ttyS[0-3]”)
dip有限的访问串口,创建到信任点的拨号 IP 连接
cdromCD-ROM, DVD+/-RW 驱动器
audio音频设备
video视频设备
scanner扫描仪
adm系统监控日志
staff一些用于初级管理工作的目录:“/usr/local”,“/home
[提示]提示

你需要属于 dialout 组才能重配置调制解调器、拨号到任意地方,等等。但如果root 用户在 “/etc/ppp/peers/” 为受信任点创建了预定义配置文件的话,你只需要属于dip 组,就可以创建拨号 IP来连接到那些受信任的点上,需使用的命令行工具包括 pppd(8)、pon(1)以及poff(1)。

某些著名的由系统提供的组允许它们的成员不带 root 权限运行特定的命令。

表 1.8. 著名的由系统提供用于特定命令运行的组列表

可访问命令
sudo不带它们的密码运行 sudo
lpadmin执行命令以从打印机数据库添加、修改、移除打印机

由系统提供的用户和组的完整列表,参见由 base-passwd包提供的“/usr/share/doc/base-passwd/users-and-groups.html”中,当前版本的“用户和组”。

用户和组系统的管理命令,参见passwd(5),group(5),shadow(5),newgrp(1),vipw(8),vigr(8),以及pam_group(8)。

1.2.6. 时间戳

GNU/Linux 文件有三种类型的时间戳。

表 1.9. 时间戳类型列表

类型含义(历史上 Unix 的定义)
mtime文件修改时间(ls -1)
ctime文件状态修改时间 (ls -lc)
atime文件最后被访问的时间 (ls -lu)
[注意]注意

ctime 不是文件创建时间。

[注意]注意

atime在 GNU/Linux 系统上的真实值可能和历史上 Unix 的定义有所不同。

  • 覆盖一个文件,将会改变该文件所有的 mtime, ctime, 和 atime 属性。

  • 改变文件的所有者或者权限,将改变文件的 ctimeatime 属性。

  • 在历史上的 Unix 系统中,读取一个文件将改变文件的 atime 属性。

  • 读一个文件,将改变文件的 atime属性;在 GNU/Linux 系统上,这仅发生在其文件系统使用“strictatime”参数挂载的情况下。

  • 如果 GNU/Linux 系统的文件系统使用 “relatime“ 选项挂载,第一次读文件,或者随后读文件,将改变该文件的 atime 属性. (从 Linux 2.6.30 开始的默认行为)

  • 如果 GNU/Linux 系统的文件系统使用 “noatime“ 挂载,则读一个文件,不会改变这个文件的 atime 属性。

[注意]注意

为了在正常的使用场景中能够提升文件系统的读取效率,新增了 “noatime“ 和 “relatime“ 这两个加载选项。如使用了 “strictatime“ 选项,即使简单的文件读操作都伴随着更新 atime 属性这个耗时的写操作。但是 atime 属性除了 mbox(5) 文件以外却很少用到。详情请看 mount(8)。

使用 touch(1) 命令修改已存在文件的时间戳。

对于时间戳,ls 命令输出字段在非英语区域(“fr_FR.UTF-8”) 与旧的区域(“C”)不同。

  1. $ LANG=fr_FR.UTF-8 ls -l foo
  2. -rw-rw-r-- 1 penguin penguin 0 oct. 16 21:35 foo
  3. $ LANG=C ls -l foo
  4. -rw-rw-r-- 1 penguin penguin 0 Oct 16 21:35 foo
[提示]提示

参考第 9.2.5 节 “定制时间和日期的显示” 自定义 “ls -l” 输出 。

1.2.7. 链接

有两种方法把一个文件 “foo” 链接到一个不同的文件名 “bar”。

请参阅下面的示例,rm 命令结果中链接数的变化和细微的差别。

  1. $ umask 002
  2. $ echo "Original Content" > 1 foo
  3. $ ls -li foo
  4. 1449840 -rw-rw-r-- 1 penguin penguin 17 Oct 16 21:42 foo
  5. $ ln foo bar # 硬链接
  6. $ ln -s foo baz # 符号链接
  7. $ ls -li foo bar baz
  8. 1449840 -rw-rw-r-- 2 penguin penguin 17 Oct 16 21:42 bar
  9. 1450180 lrwxrwxrwx 1 penguin penguin 3 Oct 16 21:47 baz -> 2 foo
  10. 1449840 -rw-rw-r-- 2 penguin penguin 17 Oct 16 21:42 foo
  11. $ rm foo
  12. $ echo "New Content" > 3 foo
  13. $ ls -li foo bar baz
  14. 1449840 -rw-rw-r-- 1 penguin penguin 17 Oct 16 21:42 bar
  15. 1450180 lrwxrwxrwx 1 penguin penguin 3 Oct 16 21:47 baz -> 4 foo
  16. 1450183 -rw-rw-r-- 1 penguin penguin 12 Oct 16 21:48 foo
  17. $ cat bar
  18. Original Content
  19. $ cat baz
  20. New Content

硬链接可以在同一个文件系统内创建,并共用同一个inode号,由ls(1)带 “-i”选项显示的。

符号链接总是名义上具有“rwxrwxrwx”的文件访问权限,如上面例子所示,实际的有效访问权限由它所指向的文件确定。

[小心]小心

除非你有非常好的理由,否则不要创建一个复杂的符号链接或硬链接通常是个好主意。符号链接的逻辑组合可能导致文件系统噩梦般的无限循环。

[注意]注意

通常使用符号链接比使用硬链接更合适,除非你有一个好理由使用硬链接。

.”目录链接到它所在的目录,因此任何新建目录的链接数从2开始。“..”目录链接到父目录,因此目录的链接数随着新的子目录的创建而增加。

如果你刚从Windows迁移到Linux,你很快将清楚 Unix 的文件名链接相较于Windows最相近的“快捷方式”是多么精心设计的。由于它是在文件系统中实现的,应用无法看到链接文件跟原始文件之间的区别。在硬链接这种情况,这真的是毫无差别。

1.2.8. 命名管道(先进先出)

命名管道是一个像管道一样的文件。你把内容放进了文件,它从另一端出来。因此,它被称为FIFO,即先进先出:你从管道这端先放进去的东西会从另一端先出来。

如果对一个命名管道进行写入操作,写入的过程不会被终止,直到写入的信息从管道中被读取出来。读取过程将会持续到没有信息可以读取为止。管道的大小始终是零,它不存储数据,它只是连接两个过程,像shell提供的 “ 1| 2” 语法功能一样。然而,一旦管道有了名称,这两个进程就可以不必在同一个命令行,甚至由同一个用户运行。管道是 UNIX 的一个非常有影响力的创新。

尝试下列例子

  1. $ cd; mkfifo mypipe
  2. $ echo "hello" >mypipe & # 放到后台运行
  3. [1] 8022
  4. $ ls -l mypipe
  5. prw-rw-r-- 1 penguin penguin 0 Oct 16 21:49 mypipe
  6. $ cat mypipe
  7. hello
  8. [1]+ Done echo "hello" >mypipe
  9. $ ls mypipe
  10. mypipe
  11. $ rm mypipe

1.2.9. 套接字

套接字被广泛应用于所有的互联网通信,数据库和操作系统本身。它类似于命名管道(FIFO)并且允许进程之间甚至不同计算机之间进行信息交换。对于套接字,这些进程不需要在同一时间运行,也不需要是同一个父进程的子进程。它是进程间通信(IPC)的一个节点。信息的交换可能会通过网络发生在不同主机之间。最常见的两种是 互联网套接字UNIX域套接字

[提示]提示

通过 “netstat -an“ 命令可以很方便的查看系统已经打开了哪些套接字。

1.2.10. 设备文件

设备文件包括系统的物理设备和虚拟设备,如硬盘、显卡、显示屏、键盘。虚拟设备的一个例子是控制台,用“/dev/console”来描述。

设备文件有两种类型。

  • 字符设备

    • 每次访问一个字符

    • 一个字符等于一个字节

    • 如键盘、串口…

  • 块设备

    • 通过更大的单元–块,进行访问

    • 一个块>一个字节

    • 如硬盘等…

你可以读写块设备文件,尽管该文件可能包含二进制数据,读取后显示出无法理解的乱码。向文件写入数据,有时可以帮助定位硬件连接故障。比如,你可以将文本文件导入打印机设备“/dev/lp0”,或者将调制解调命令发送到合适的串口“/dev/ttyS0”。但是,除非这些操作都小心完成,否则可能会导致一场大灾难。所以要特别小心。

[注意]注意

常规访问打印机,使用lp(1)。

设备的节点数可以通过执行ls(1)得到,如下所示。

  1. $ ls -l /dev/sda /dev/sr0 /dev/ttyS0 /dev/zero
  2. brw-rw---T 1 root disk 8, 0 Oct 16 20:57 /dev/sda
  3. brw-rw---T+ 1 root cdrom 11, 0 Oct 16 21:53 /dev/sr0
  4. crw-rw---T 1 root dialout 4, 64 Oct 16 20:57 /dev/ttyS0
  5. crw-rw-rw- 1 root root 1, 5 Oct 16 20:57 /dev/zero
  • /dev/sda“的主设备号是8,次设备号是0。它可以被disk群组的用户读写。

  • /dev/sr0“的主设备号是11,次设备号是0。它可以被cdrom群组的用户读写。

  • /dev/ttyS0“的主设备号是4,次设备号是64。它可以被dailout群组的用户读写。

  • /dev/zero“的主设备号是1,次设备号是5。它可以被任意用户读写。

在现代Linux系统中,处在”/dev“之下的文件系统会自动被udev()机制填充。

1.2.11. 特殊设备文件

还有一些特殊的设备文件。

表 1.10. 特殊设备文件列表

设备文件操作响应描述
/dev/null读取返回“文件结尾字符(EOF)“
/dev/null写入无返回(一个无底的数据转存深渊)
/dev/zero读取返回”\0空字符”(与ASCII中的数字0不同)
/dev/random读取从真随机数产生器返回一个随机字符,供应真熵(缓慢)
/dev/urandom读取从能够安全加密的伪随机数产生器返回一个随机字符
/dev/full写入返回磁盘已满(ENOSPC)错误

这些特别设备文件经常和shell数据重定向联合使用(参考第 1.5.8 节 “典型的顺序命令和 shell 重定向”)。

1.2.12. procfs 和 sysfs

procfssysfs两个伪文件系统,分别加载于”/proc“和”/sys“之上,将内核中的数据结构暴露给用户空间。或者说,这些条目是虚拟的,他们打开了深入了解操作系统运行的方便之门。

目录”/proc“为每个正在运行的进程提供了一个子目录,目录的名字就是进程标识符(PID)。需要读取进程信息的系统工具,如ps(),可以从这个目录结构获得信息。

/proc/sys“之下的目录,包含了可以更改某些内核运行参数的接口。(你也可以使用专门的sysctl()命令修改,或者使用其预加载/配置文件”/etc/sysctl.conf“。)

当人们看到这个特别大的文件”/proc/kcore“时,常常会惊慌失措。这个文件于你的的电脑内存大小相差不多。它被用来调试内核。它是一个虚拟文件,指向系统内存,所以不必担心它的大小。

/sys“以下的目录包含了内核输出的数据结构,它们的属性,以及它们之间的链接。它同时也包含了改变某些内核运行时参数的接口。

参考”proc.txt(.gz)“,”sysfs.txt(.gz)“,以及其他相关的Linux内核文档(”/usr/share/doc/linux-doc-*/Documentation/filesystems/*“),这些文件由linux-doc-*软件包提供。

1.2.13. tmpfs

tmpfs是一个临时文件系统,它的文件都保存在虚拟内存中。必要时,位于内存页缓存的tmpfs数据可能被交换到硬盘中的交换分区

系统启动早期阶段,”/run“目录挂载为tmpfs。这样即使”/“挂载为只读,它也是可以被写入的。它为过渡态文件提供了新的存储空间,同时也替代了Filesystem Hierarchy Standar2.3版中说明的目录位置:

  • /var/run“ → “/run

  • /var/lock“ → “/run/lock

  • /dev/shm“ → “/run/shm

参考”tmpfs.txt(.gz)“, 文件位于Linux内核文档(”/usr/share/doc/linux-doc-*/Documentation/filesystems/*“)目录之下,由软件包linux-doc-*提供。

1.3. Midnight Commander (MC)

Midnight Commander (MC) 是一个Linux终端或其它终端环境下的 GNU 版 “瑞士军刀” 。它为新手们提供了一个菜单式样的终端使用体验,这更易于学习运用标准的 Unix 命令。

你可能需要按照下面的命令来安装标题为 “ mc “ 的Midnight Commander 包.

  1. $ sudo apt-get install mc

使用 mc(1) 命令那个来浏览 Debian 系统。这是最好的学习方式。请使用光标键和回车键来翻看一些感兴趣的内容。

  • /etc“ 及其子目录

  • /var/log “ 及其子目录

  • /usr/share/doc “ 及其子目录

  • /sbin “ 和 “ /bin

1.3.1. 自定义 MC

为了在退出 MC 的时候更改目录并 cd 到其它目录,我建议修改 “~/.bashrc“ 包含一个由 mc 包提供的脚本。

  1. . /usr/lib/mc/mc.sh

查看mc(1) (在 “-P“ 选项里) 的原因。 (如果你不能理解我这里说所讲的,你可以稍后回头再看)

1.3.2. 启动 MC

MC 可以这样启动起来。

  1. $ mc

MC 通过菜单覆盖了所有的文件操作,因此而让用户更省心省力。只需要按 F1 就可以跳转到帮助界面。你只需要按光标键和功能键就可以使用 MC。

[注意]注意

某些终端比如 gnome-terminal(1),功能键的按键触发消息可能会被终端程序截取。在 gnome-terminal 里可以通过 “Edit” → “Keyboard Shortcuts” 选项设置来禁止这类消息。

如果你遇到字符编码问题,显示出来都是乱码,通过添加”-a“到 MC 命令行或许有助于避免此类问题。

如果这样不能解决 MC 中的显示问题,可以参考 第 9.4.6 节 “终端配置”.

1.3.3. MC 文件管理

默认的两个目录面板里包含了文件列表。另一个有用的模式是设置右边窗口为 “信息” 来读取文件访问权限信息。接下来是一些必要的快捷键。守护进程gpm(8)运行的时候,你也可以在字符命令行里用鼠标来操作。 (在 MC 里进行复制和粘贴操作的时候一定要按住 shift 键。)

表 1.11. MC 快捷键绑定

快捷键键绑定功能
F1帮助菜单
F3内部文件查看器
F4内部编辑器
F9激活下拉菜单
F10退出 Midnight Commander
Tab在两个窗口间移动
InsertCtrl-T用于多文件操作的标记文件,如副本
Del删除文件 (注意—-设置 MC 为安全删除模式)
光标键自我解释

1.3.4. MC 命令行技巧

  • cd 命令在选中的屏幕中改变目录。

  • Ctrl-Enter or Alt-Enter 拷贝文件名到命令行。使用 cp(1) 和 mv(1) 两个命令来进行处理。

  • Alt-Tab 显示文件名自动补全提示。

  • 通过添加 MC 命令参数可以指定开始目录;例如,”mc /etc /root“。

  • Esc + n-keyFn (i.e., Esc + 1F1, etc.; Esc + 0F10)

  • 先按 Esc 键 和同时按 Alt 是一样;例如, 输入 Esc + c 和同时 Alt-C是一样的。Esc 被称为 meta 键,有时候也称之为 “M-“。

1.3.5. MC 内部编辑器

这个内置编辑器有一个有意思的粘贴方案。摁 F3 开始选择起始点,再摁 F3 选择终点并高亮选择区。此刻你可以移动你的光标,使用 F6 将选区移动到当前光标下,F5 则将选区复制到当前光标下。 F2 保存文件。 F10 退出。多数光标键以直观的方式工作。

MC 编辑器可以直接以下面的命令方式启动。

  1. $ mc -e filename_to_edit
  1. $ mcedit filename_to_edit

这不是一个多窗口编辑器,但是能通过复用终端来达到同样的效果。在两个窗口间复制,需要用到 Alt-F < n > 来切换虚拟终端并使用 “File→Insert file” 或者 “File→Copy to file” 来移动文本。

内部编辑器可以被外部编辑器替代。

同样,许多程序使用环境变量$EDITOR$VISUAL来决定编辑器的使用。如果你准备使用vim(1)或者nano(1)来开始,你或许需要将下面的代码加入”~/.bashrc“来对mcedit进行设置。

  1. export EDITOR=mcedit
  2. export VISUAL=mcedit

如果可能的话我推荐用 “vim“。

如果你使用vim(1)并不顺手,你可以在大部分系统中继续使用mcedit(1)来进行工作。

1.3.6. MC 内部查看器

MC是一个非常智能的查看器。这是一个在文档中搜索文本的好工具。我经常使用它在/usr/share/doc目录中查找文件。这是浏览大量Linux信息的最快方式。这个查看器可以通过下列命令中的任何一个来直接启动。

  1. $ mc -v path/to/filename_to_view
  1. $ mcview path/to/filename_to_view

1.3.7. 自动启动 MC

在文件中输入回车, 用适当的程序来处理文件的内容 (查看 第 9.3.11 节 “自定义被启动的程序”)。这是 MC 一个非常方便的用法。

表 1.12. MC 中对回车键的响应

文件类型对回车键的响应
可执行文件执行命令
帮助文档管道内容查看器软件
html 文件管道内容网页浏览器
.tar.gz“ 和 “.deb“ 文件浏览其内容就像查看子目录一样

为让这些查看器和虚拟文件特征生效,可查看的文件不能够被设置为可执行。使用 chmod(1) 或通过 MC 文件菜单改变他们的状态。

1.3.8. MC 中的 FTP 虚拟文件系统

MC 能够使用 FTP 跨因特网访问文件。在菜单按 F9, 然后输入 “p“ 来激活 FTP 虚拟文件系统。按 “username:passwd@hostname.domainname“ 的形式输入 URL,就会像本地目录一样来检索远程目录。

试着打开 “[deb.debian.org/debian]“ 来浏览 Debian 的文件组织结构。

1.4. 类 Unix 工作环境基础

虽然 MC 差不多可以让你做任何事情,但学会从 shell 提示下使用命令行工具也是非常重要的,可以让你变得熟悉类 Unix 工作环境。

1.4.1. 登录 shell

你可以通过 chsh(1) 选择你的登录 shell。

表 1.13. shell 程序列表

软件包流行度大小POSIX shell说明
bashV:791, I:9996469YesBash: GNU Bourne Again SHell (事实上的标准)
tcshV:9, I:291316NoTENEX C Shell: 一个 Berkeley csh 的增强版本
dashV:907, I:992221YesDebian Almquist Shell, 擅长 shell 脚本
zshV:37, I:732442YesZ shell:有许多增强的标准 shell
mkshV:4, I:121469YesKorn shell 的一个版本
cshV:2, I:8343NoOpenBSD C Shell, Berkeley csh 的一个版本
sashV:0, I:61054Yes有内置命令的 Stand-alone shell (并不意味着标准的 “/bin/sh“)
kshV:3, I:163284YesKorn shell的真正的 AT&T 版本
rcV:0, I:2169NoAT&T Plan 9 rc shell 的一个实现
poshV:0, I:0190YesPolicy-compliant Ordinary SHell 策略兼容的普通 shell(pdksh 派生)
[提示]提示

虽然类 POSIX 共享基本语法,但他们在 shell 变量和全局扩展等基本事情上,行为可以不同。细节请查阅他们的文档。

在本教程中,交互式的 shell 总是指 bash.

1.4.2. 定制bash

你可以通过“~/.bashrc”来定制bash(1)的行为。

尝试下列例子。

  1. # enable bash-completion
  2. if ! shopt -oq posix; then
  3. if [ -f /usr/share/bash-completion/bash_completion ]; then
  4. . /usr/share/bash-completion/bash_completion
  5. elif [ -f /etc/bash_completion ]; then
  6. . /etc/bash_completion
  7. fi
  8. fi
  9. # CD upon exiting MC
  10. . /usr/lib/mc/mc.sh
  11. # set CDPATH to a good one
  12. CDPATH=.:/usr/share/doc:~:~/Desktop:~
  13. export CDPATH
  14. PATH="${PATH+$PATH:}/usr/sbin:/sbin"
  15. # set PATH so it includes user's private bin if it exists
  16. if [ -d ~/bin ] ; then
  17. PATH="~/bin${PATH+:$PATH}"
  18. fi
  19. export PATH
  20. EDITOR=vim
  21. export EDITOR
[提示]提示

你可以在第 9 章 系统技巧中的第 9.2.7 节 “有颜色输出的命令”找到更多关于bash的定制技巧。

[提示]提示

bash-completion 软件包能够让 bash 进行命令补全.

1.4.3. 特殊按键

类Unix环境,有一些具有特殊含义的按键。请注意,普通的Linux字符控制台,只有左手边的CtrlAlt键可以正常工作。其中有几个值得记住的按键。

表 1.14. bash的按键绑定列表

快捷键描述
Ctrl-U删除光标前到行首的字符
Ctrl-H删除光标前的一个字符
Ctrl-D终止输入(如果你在使用shell,则退出shell)
Ctrl-C终止一个正在运行的程序
Ctrl-Z通过将程序移动到后台来暂停程序
Ctrl-S停止屏幕输出
Ctrl-Q激活屏幕输出
Ctrl-Alt-Del重启/关闭系统,参见inittab(5)
Left-Alt-key(或Windows-keyEmacs和相似UI的元键(meta-key)
Up-arrow开始在bash中的命令历史搜索
Ctrl-R开始在bash中的增量命令历史搜索
Tabbash 命令行中补全文件名
Ctrl-V Tabbash 命令行中输出 Tab 而不是进行补全
[提示]提示

Ctrl-S的终端功能可能被stty(1)禁用。

1.4.4. Unix类型的鼠标操作

Unix类型的鼠标操作基于3键的鼠标系统。

表 1.15. Unix样式的鼠标操作列表

操作响应
左击并拖动鼠标选择并复制到剪贴板
单击左键选择开头
单击右键选择末尾并拷贝到剪贴板
单击中键粘贴剪切板的内容到光标处

在现代滚轮鼠标上的中央滚轮,被认为是中间键,并可以被当做中间键使用。在 2 键鼠标系统的情况下,同时按左键和右键就相当于按中间键。为了在 Linux 字符控制台使用鼠标,你需要把 gpm( 8) 作为后台守护进程(daemon)运行。

1.4.5. 分页程序

less(1) 命令是一个增强版的分页程序(文件内容查看器)。它按照指定的命令参数或标准输出来读取文件。在用 less 命令查看的时候如果需要帮助可以按 “h”。它的功能比 more(1) 命令更丰富,通过在脚本的开头执行 “eval $(lesspipe)“ 或 “eval $(lessfile)“ 它的功能还能变得更加强大。详细请参考 “/usr/share/doc/less/LESSOPEN“。 “-R“ 选项可以实现原始的字符输出还可以启用 ANSI 颜色转义序列。详细请参考 less(1)。

1.4.6. 文本编辑器

在使用类 Unix 系统过程中, 各种类似于Vim) 或 Emacs的工具,你应该精通其中的一个。

我认为习惯于使用 Vim 命令是一个明智的选择,因为Linux/Unix系统里一般都附带了 Vi 编辑器。 (实际上最初的vi 以及后来的 nvi 这类工具程序很常见。因为在 Vim 里提供了F1帮助键,在同类工具中它的功能更强大,所以我选择 Vim 而不是其它新出的一些工具。)

假设你不是用 Emacs 就是用XEmacs 作为你的编辑器,其实还有更好的选择,尤其是在编程的时候。 Emacs 还有很多其他的特点,包括新手导读,目录编辑器,邮件客户端等等。当编写脚本或程序的时候,它能自动识别当前工作模式所对应的格式,让使用更加便利。一些人甚至坚持认为Linux系统里最需要配备的就是 Emacs。花十分钟来学习 Emacs 可以为后面的工作剩下更多时间。在此强烈推荐学习使用 Emacs 时候直接使用 GNU Emacs 参考手册。

在实践应用中所有这些程序都会有一个教程,输入 “vim“ 和F1键就可以启动Vim。建议你最好阅读一下前面的35行。移动光标到 “|tutor|“ 并按 Ctrl-] 就可以看到在线培训教程。

[注意]注意

好的编辑器,像 Vim 和 Emacs,可以处理 UTF-8 及其它不常用编码格式的文本。有个建议就是在 X 环境下使用UTF-8编码,并安装要求的程序和字体。编辑器里可以选择独立于 X 环境的编码格式。关于多字节文本可以查阅参考文档。

1.4.7. 设置默认文本编辑器

Debian有许多不同的编辑器。我们建议安装上面提到的vim软件包。

Debian通过命令“/usr/bin/editor”提供了对系统默认编辑器的统一访问,因此其它程序(例如reportbug(1))可以调用它。你可以通过下列命令改变它。

  1. $ sudo update-alternatives --config editor

对于新手,我建议使用“/usr/bin/vim.basic”代替“/usr/bin/vim.tiny”,因为它支持格式高亮。

[提示]提示

许多程序使用环境变量“$EDITOR”或“$VISUAL”来决定使用那个编辑器(参见第 1.3.5 节 “MC 内部编辑器”第 9.3.11 节 “自定义被启动的程序”)。出于Debian系统的一致性考虑,它们被设置到“/usr/bin/editor”。(在历史上,“$EDITOR”是“ed”,“$VISUAL”是“vi”。)

1.4.8. 定制vim

你可以通过“~/.vimrc”来定制vim(1)的行为。

尝试下列例子

  1. " -------------------------------
  2. " Local configuration
  3. "
  4. set nocompatible
  5. set nopaste
  6. set pastetoggle=<f2>
  7. syn on
  8. if $USER == "root"
  9. set nomodeline
  10. set noswapfile
  11. else
  12. set modeline
  13. set swapfile
  14. endif
  15. " filler to avoid the line above being recognized as a modeline
  16. " filler
  17. " filler

1.4.9. 记录 shell 活动

shell命令的输出有可能滚动出了屏幕,并可能导致你无法再查看到它。将shell活动记录到文件中再来回顾它是个不错的主意。当你执行任何系统管理任务时,这种记录是必不可少的。

记录 shell 活动的基本方法是在script(1)下运行 shell。

尝试下列例子

  1. $ script
  2. Script started, file is typescript

script下使用任何shell命令。

Ctrl-D来退出script

  1. $ vim typescript

参见第 9.2.3 节 “清晰的记录 shell 活动”

1.4.10. 基本的Unix命令

让我们来学习基本的Unix命令。在这里,我指的是一般意义上的“UNIX”。任何UNIX克隆系统通常都会提供等价的命令。Debian系统也不例外。如果有一些命令不像你想的那样起作用,请不要担心。如果shell中使用了别名,其对应的命令输出会不同。这些例子并不意味着要以这个顺序来执行。

尝试使用非特权用户账号来使用下列的命令。

表 1.16. 基本的Unix命令列表

命令说明
pwd显示当前/工作目录的名称
whoami显示当前的用户名
id显示当前用户的身份(名称、uid、gid和相关组)
file <foo>显示“<foo>”文件的文件类型
type -p <commandname>显示“<commandname>”命令的文件所处位置
which <commandname>同上
type <commandname>显示“<commandname>”命令的相关信息
apropos <key-word>查找与“<key-word>”有关的命令
man -k <key-word>同上
whatis <commandname>用一行解释 “<commandname>” 命令
man -a <commandname>显示“<commandname>”命令的解释(Unix风格)
info <commandname>显示“<commandname>”命令相当长的解释(GNU风格)
ls显示目录内容(不包含以 . 点号开头的文件和目录)
ls -a显示目录内容(包含所有文件和目录)
ls -A显示目录内容(包含几乎所有文件和目录,除了“..”和“.”)
ls -la显示所有的目录内容,并包含详细的信息
ls -lai显示所有的目录内容,并包含inode和详细的信息
ls -d显示当前目录下的所有目录
tree使用树状图显示目录内容
lsof <foo>列出处于打开状态的文件 “<foo>
lsof -p <pid>列出被某进程打开的文件: “<pid>
mkdir <foo>在当前目录中建立新目录“<foo>
rmdir <foo>删除当前目录中的“<foo>”目录
cd <foo>切换到当前目录下或变量“$CDPATH”中的“<foo>”目录
cd /切换到根目录
cd切换到当前用户的家目录
cd /<foo>切换到绝对路径为“/<foo>”的目录
cd ..切换到上一级目录
cd ~<foo>切换到用户“<foo>”的家目录
cd -切换到之前的目录
</etc/motd pager使用默认的分页程序来显示“/etc/motd”的内容
touch <junkfile>建立一个空文件“<junkfile>
cp <foo> <bar>将一个现有文件“<foo>”复制到一个新文件“<bar>
rm <junkfile>删除文件“<junkfile>
mv <foo> <bar>将一个现有文件“<foo>”重命名成“<bar>”(“<bar>”必须不存在)
mv <foo> <bar>将一个现有文件“<foo>”移动到新的位置“<bar>/<foo>”(必须存在“<bar>”目录)
mv <foo> <bar>/<baz>移动一个现有文件“<foo>”到新位置并重命名为“<bar>/<baz>”(必须存在“bar”目录,且不存在“bar>/<baz>文件”)
chmod 600 <foo>使其他人无法读写现有文件“<foo>”(并且所有人都无法执行该文件)
chmod 644 <foo>使其他人对现有文件“<foo>”可读但不可写(并且所有人都无法执行该文件)
chmod 755 <foo>使其他人对“<foo>”可读而不可写(并且所有人都能执行该文件)
find . -name <pattern>使用 shell “<pattern>” 查找匹配的文件名(速度较慢)
locate -d . <pattern>使用 shell “<pattern>” 查找匹配的文件名(速度较快,使用定期生成的数据库)
grep -e “<pattern>” .html在当前目录下以“.html”结尾的所有文件中,查找匹配“<pattern>”的文件并显示
top全屏显示进程信息,输入“q”退出
ps aux | pager显示所有正在运行的进程的信息(BSD风格)
ps -ef | pager显示所有正在运行的进程的信息(Unix system-V风格)
ps aux | grep -e “[e]xim4“显示所有正在运行“exim”和“exim4”的进程
ps axf | pager显示所有正在运行的进程的信息(ASCII风格)
kill <1234>杀死ID为“<1234>”的进程
gzip <foo>使用 Lempel-Ziv 编码(LZ77)将“<foo>”压缩为“<foo>.gz
gunzip <foo>.gz将“<foo>.gz”解压为“<foo>
bzip2 <foo>使用 Burrows-Wheeter 块排序压缩算法和 Huffman 编码将“<foo>”压缩为“<foo>.bz2”(压缩效果比gzip更好)
bunzip2 <foo>.bz2将“<foo>.bz2”解压为“<foo>
xz <foo>使用 Lempel-Ziv-Markov 链算法将“<foo>”压缩为“<foo>.xz”(压缩效果比bzip2更好)
unxz <foo>.xz将“<foo>.xz”解压为“<foo>
tar -xvf <foo>.tar从“<foo>.tar”档案中提取文件
tar -xvzf <foo>.tar.gz从被gzip压缩过的“<foo>.tar.gz”档案中提取文件
tar -xvjf <foo>.tar.bz2从“<foo>.tar.bz2”档案中提取文件
tar -xvJf <foo>.tar.xz从“<foo>.tar.xz”档案中提取文件
tar -cvf <foo>.tar <bar>/将目录“<bar>/”中的内容打包到“<foo>.tar”档案中
tar -cvzf <foo>.tar.gz <bar>/将目录 “<bar>/” 中的内容打包并压缩成 “<foo>.tar.gz” 文件
tar -cvjf <foo>.tar.bz2 <bar>/将目录“<bar>/”中的内容打包到“<foo>.tar.bz2”档案中
tar -cvJf <foo>.tar.xz <bar>/将目录”<bar>/“中的内容打包到”<foo>.tar.xz“档案中
zcat README.gz | pager使用默认的分页程序来显示 “README.gz” 压缩包中的内容
zcat README.gz > foo将“README.gz”解压后的内容输出到文件“foo”中
zcat README.gz >> foo将“README.gz”解压后的内容添加到文件“foo”的末尾(如果文件不存在,则会先建立该文件)
[注意]注意

Unix有一个惯例,以“.”开头的文件将被隐藏。它们一般为包含了配置信息和用户首选项的文件。

[注意]注意

对于cd命令,参见builtins(7)。

[注意]注意

基本的 Debian 系统的默认分页程序是 more(1),它无法往回滚动。通过命令 “apt-get install less“ 安装 less 软件包后,less(1) 会成为默认的分页程序,它可以通过方向键往回滚动。

[注意]注意

[“ 和”]“ 在正则表达式 “ps aux | grep -e “[e]xim4“ 命令中,可以避免grep在结果中排除它自己, 正则表达式中的 “4“ 意思是空或字符”4“ ,这样可以让 grep 既找到 “exim“ 也找到 “exim4“。 虽然 “*“ 可以用于命令名称匹配和正则表达式中,但是它们的含义是不一样的。欲详细了解正则表达式可以参考 grep(1)。

作为训练,请使用上述的命令来遍历目录并探究系统。如果你有任何有关控制台命令的问题,请务必阅读手册。

尝试下列例子

  1. $ man man
  2. $ man bash
  3. $ man builtins
  4. $ man grep
  5. $ man ls

手册的风格可能让人有点难以习惯,因为它们都相当简洁,尤其是比较老旧、非常传统的那些手册。但是,一旦你习惯了它,你来欣赏它们的简洁。

请注意,许多类Unix命令(包含来自 GNU 和 BSD 的)都可以显示简短的帮助信息,你可以使用下列的其中一种方式来查看它(有时不带任何参数也可以)。

  1. $ <commandname> --help
  2. $ <commandname> -h

1.5. 简单 shell 命令

现在,你对如何使用 Debian 系统已经有一些感觉了。让我们更深入了解 Debian 系统的命令执行机制。在这里,我将为新手做一般的讲解。精确的解释参见bash(1)。

一般的命令由有序的组件构成。

  1. 设置变量值(可选)

  2. 命令名

  3. 参数(可选)

  4. 重定向(可选:> , >> , < , << 等等)

  5. 控制操作(可选:&&|| , <换行符> , ; , & , ( , )

1.5.1. 命令执行和环境变量

一些环境变量的值会改变部分Unix命令的行为。

环境变量的默认值由PAM系统初始化,其中一些会被某些应用程序重新设定。

  • 显示管理器(例如gdm3)会重新设定环境变量。

  • Shell脚本启动的时候会重置 “~/.bash_profile“ 和 “~/.bashrc“ 中的环境变量。

1.5.2. “$LANG”变量

$LANG” 变量的完整的语言环境值由3部分组成:“xx_YY.ZZZZ”。

表 1.17. 语言环境值的 3 个部分

语言环境值说明
xxISO 639 语言代码(小写)例如“en”
YYISO 3166 国家代码(大写)例如“US”
ZZZZ编码,总是设置为“UTF-8”

对于语言代码和国家代码,参见“info gettext”中的相关描述。

对于现代 Debian 系统中的编码,你应该总是设定为UTF-8,除非你有足够的理由和背景知识并且特别想使用过时的编码。

对于语言环境配置的细节,参见 第 8.4 节 “语言环境”

[注意]注意

在 Debian 系统中,请确认安装 locales-all 软件包来使用所有的语言环境。

[注意]注意

LANG=en_US” 既不是 “LANG=C” 也不是 “LANG=en_US.UTF-8”。它是 “LANG=en_US.ISO-8859-1”(参见第 8.4.1 节 “编码的基础知识”)。

表 1.18. 语言环境推荐列表

语言环境推荐语言(地区)
en_US.UTF-8英语(美国)
en_GB.UTF-8英语(大不列颠)
fr_FR.UTF-8法语(法国)
de_DE.UTF-8德语(德国)
it_IT.UTF-8意大利语(意大利)
es_ES.UTF-8西班牙语(西班牙)
ca_ES.UTF-8加泰隆语(西班牙)
sv_SE.UTF-8瑞典语(瑞典)
pt_BR.UTF-8葡萄牙语(巴西)
ru_RU.UTF-8俄语(俄国)
zh_CN.UTF-8汉语(中华人民共和国)
zh_TW.UTF-8汉语(中国台湾)
ja_JP.UTF-8日语(日本)
ko_KR.UTF-8韩语(韩国)
vi_VN.UTF-8越南语(越南)

使用 shell 命令行按顺序执行下列典型的命令。

  1. $ date
  2. Sun Jun 3 10:27:39 CST 2007
  3. $ LANG=fr_FR.UTF-8 date
  4. dimanche 3 juin 2007, 10:27:33 (UTC+0800)

这里,date(1)程序执行时使用了与环境变量“$LANG”不同的值。

  • 第一个命令,“$LANG” 设置为系统的默认语言环境值 “en_US.UTF-8”。

  • 第二个命令,“$LANG”设置为法语的 UTF-8 语言环境值“fr_FR.UTF-8”。

大多数的命令在执行时并没有预先定义环境变量。对于上面的例子,你也可以选择如下的方式。

  1. $ LANG=fr_FR.UTF-8
  2. $ date
  3. dimanche 3 juin 2007, 10:27:33 (UTC+0900)

正如你所看到的,命令的输出受环境变量的影响,上面产生的是法语输出。如果你想这个环境变量能在子进程中被继承的话(例如执行 shell 脚本时),你需要使用下面的命令导出(export)它。

  1. $ export LANG
[注意]注意

在使用常规的控制台终端的时候,环境变量 “$LANG“ 通常会被桌面环境变量通过 exported 方式重置。如果要测试 export 带来的影响,这个可能不是一个很好的例子。

[提示]提示

提交一个BUG报告的时候,如果使用的是非英语的环境,在 “LANG=en_US.UTF-8“ 环境下对命令进行运行和检查会更好一些。

可以通过命令 locale(5) 和locale(7) 来查看 “$LANG“ 及相关的环境变量。

[注意]注意

建议最好用变量 “$LANG“ 来配置系统环境变量,只有在逼不得已的情况下才用 $LC_*“ 开头的变量。

1.5.3. “$PATH“ 变量

当你在 Shell 里输入命令的时候,Shell 会在 “$PATH“ 变量所包含的目录列表里进行搜索,”$PATH“ 变量的值也叫作 Shell 的搜索路径。

在默认的 Debian 安装过程中,所使用的用户账号的 “$PATH“ 环境变量可能不包括 “/sbin“ 和”/usr/sbin“目录。例如,ifconfig 命令就需要指定完整的路径 “/sbin/ifconfig“。(类似地, ip 命令是在 “/bin“ 目录下)

可以在 Bash 脚本文件 “~/.bash_profile“ 或 “~/.bashrc“ 中改变 “$PATH“ 环境变量的值。

1.5.4. “$HOME“ 变量

很多命令在用户目录中都存放了用户指定的配置,然后通过配置的内容来改变它的执行方式,用户目录通常用 “$HOME“ 变量来指定。

表 1.19. “$HOME“ 变量值列表

$HOME“ 变量的值程序运行环境
/初始进程执行的程序(守护进程)
/rootroot 用户权限 Shell 执行的程序
/home/<normal_user>普通用户权限Shell执行的程序
/home/<normal_user>普通用户 GUI 桌面菜单执行的程序
/home/<normal_user>用root用户权限来执行程序 “sudo program
/root用 root 用户权限执行程序 “sudo -H program
[提示]提示

Shell 扩展 “~/“ 为转入当前用户的主目录,也就是”$HOME/“。Shell 扩展 “~foo/“ 为foo 的目录,也就是 “/home/foo/“。

1.5.5. 命令行选项

一些命令附带参数。这些参数以 “-“ 或 “--“ 开头,通常称之为选项,用来控制命令的执行方式。

  1. $ date
  2. Mon Oct 27 23:02:09 CET 2003
  3. $ date -R
  4. Mon, 27 Oct 2003 23:02:40 +0100

这里的命令参数 “-R“ 改变 date(1) 命令输出为 RFC2822 标准的日期字符格式。

1.5.6. Shell 通配符

经常有这种情况你期望命令成串自动执行而不需要挨个输入,将文件名扩展为 glob,(有时候被称为 通配符),以此来满足这方面的需求。

表 1.20. Shell glob 模式

shell glob 模式匹配规则描述
不以 “.“ 开头的文件名(段)
.以 “.“ 开头的文件名(段)
?精确字符
[…]包含在括号中的任意字符都可以作为精确字符
[a-z]a“ 到 “z“ 之间的任意一个字符都可以作为精确字符
[^…]除了包含在括号中的任意字符 ( “ 1^ 2”除外 ),其它字符都可以作为精确字符

尝试下列例子

  1. $ mkdir junk; cd junk; touch 1.txt 2.txt 3.c 4.h .5.txt ..6.txt
  2. $ echo *.txt
  3. 1.txt 2.txt
  4. $ echo *
  5. 1.txt 2.txt 3.c 4.h
  6. $ echo *.[hc]
  7. 3.c 4.h
  8. $ echo .*
  9. . .. .5.txt ..6.txt
  10. $ echo .*[^.]*
  11. .5.txt ..6.txt
  12. $ echo [^1-3]*
  13. 4.h
  14. $ cd ..; rm -rf junk

参见 glob(7)。

[注意]注意

与 shell 通用的文件名匹配方式不同, 使用 “ -name “ 选项的 find (1),其 shell 模式” * “ ,匹配以” . “开始的文件名.(新 POSIX 的特性)

[注意]注意

BASH 可以使用内置的 shopt 选项如 “ dotglob “,” noglob “,” nocaseglob “,” nullglob “,” extglob “定制全局行为,使用 bash ( 1 )查看详细说明。

1.5.7. 命令的返回值

每个命令都会返回它的退出状态(变量:“$?”)作为返回值。

表 1.21. 命令的退出代码

命令的退出状态数字返回值逻辑返回值
successzero, 0TRUE
errornon-zero, -1FALSE

尝试下列例子。

  1. $ [ 1 = 1 ] ; echo $?
  2. 0
  3. $ [ 1 = 2 ] ; echo $?
  4. 1
[注意]注意

请注意,success 是逻辑 TRUE ,0(zero)则是它的值。这有些不直观,需要在这里提一下。

1.5.8. 典型的顺序命令和 shell 重定向

让我们试着记住下面 Shell 命令里部分命令行所使用的命令习语。

表 1.22. Shell 命令常见用法

命令常见用法说明
command &在子 shell 的后台 中执行 command
command1 | command2通过管道command1 的标准输出作为 command2 的标准输入(并行执行)
command1 2>&1 | command2通过管道command1 的标准输出和标准错误作为 command2 的标准输入(并行执行)
command1 ; command2顺序执行 command1command2
command1 && command2执行 command1;如果成功,按顺序执行 command2(如果 command1 command2 都执行成功了,返回 success )
command1 || command2执行 command1;如果不成功,按顺序执行 command2(如果 command1 command2 执行成功,返回 success )
command > foocommand 的标准输出重定向到文件 foo(覆盖)
command 2> foocommand 的标准错误重定向到文件 foo(覆盖)
command >> foocommand 的标准输出重定向到文件 foo(附加)
command 2>> foocommand 的标准错误重定向到文件 foo(附加)
command > foo 2>&1command 的标准输出和标准错误重定向到文件 foo
command < foocommand 的标准输入重定向到文件 foo
command << delimitercommand 的标准输入重定向到下面的命令行,直到遇到“delimiter”(here document)
command <<- delimitercommand 的标准输入重定向到下面的命令行,直到遇到“delimiter”(here document,命令行中开头的制表符会被忽略)

Debian 系统是一个多任务的操作系统。后台任务让用户能够在一个 shell 中执行多个程序。后台进程的管理涉及 shell 的内建命令:jobsfgbgkill。请阅读 bash(1) 中的章节:“SIGNALS”、“JOB CONTROL” 和 “builtins(1)”。

尝试下列例子

  1. $ </etc/motd pager
  1. $ pager </etc/motd
  1. $ pager /etc/motd
  1. $ cat /etc/motd | pager

尽管4个 shell 重定向的例子都会显示相同的结果,但最后一个例子毫无意义地运行了额外的 cat 命令浪费了资源。

shell 允许你使用 exec 通过任意一个文件描述符来打开文件。

  1. $ echo Hello >foo
  2. $ exec 3<foo 4>bar # 打开文件
  3. $ cat <&3 >&4 # 标准输入重定向到 3, 标准输出重定向到 4
  4. $ exec 3<&- 4>&- # 关闭文件
  5. $ cat bar
  6. Hello

预定义的文件描述符0-2。

表 1.23. 预定义的文件描述符

设备说明文件描述符
stdin标准输入0
stdout标准输出1
stderr标准错误2

1.5.9. 命令别名

你可以为经常使用的命令设置一个别名。

尝试下列例子

  1. $ alias la='ls -la'

现在,“la”是“ls -al”的简写形式,并同样会以长列表形式列出所有的文件。

你可以使用 alias 来列出所有的别名(参见 bash(1) 中的“SHELL BUILTIN COMMANDS”)。

  1. $ alias
  2. ...
  3. alias la='ls -la'

你可以使用 type 来确认命令的准确路径或类型(参见 bash(1) 中的“SHELL BUILTIN COMMANDS”)。

尝试下列例子

  1. $ type ls
  2. ls is hashed (/bin/ls)
  3. $ type la
  4. la is aliased to ls -la
  5. $ type echo
  6. echo is a shell builtin
  7. $ type file
  8. file is /usr/bin/file

ls 在最近被使用过,而 “file” 没有,因此 “ls” 标记为 “hashed”(被录入哈希表),即 shell 有一个内部的记录用来快速访问 “ls” 所处的位置。

[提示]提示

参见第 9.2.7 节 “有颜色输出的命令”

1.6. 类 Unix 的文本处理

在类 Unix 的工作环境中,文本处理是通过使用管道组成的标准文本处理工具链完成的。这是另一个重要的 Unix 创新。

1.6.1. Unix 文本工具

这里有一些在类 Unix 系统中经常使用到的标准文本处理工具。

  • 没有使用正则表达式:

    • cat(1) 连接文件并输出全部的内容。

    • tac(1) 连接文件并反向输出。

    • cut(1) 选择行的一部分并输出。

    • head(1) 输出文件的开头。

    • tail(1) 输出文件的末尾。

    • sort(1) 对文本文件的行进行排序。

    • uniq(1) 从已排序的文件中移除相同的行。

    • tr(1) 转换或删除字符。

    • diff(1) 对文件的行进行对比。

  • 使用基础的正则表达式( BRE ):

    • grep(1) 匹配满足 pattern 的文本。

    • ed(1) 是一个原始行编辑器。

    • sed(1) 是一个流编辑器。

    • vim(1) 是一个屏幕编辑器。

    • emacs(1) 是一个屏幕编辑器。(有些扩展的 BRE

    • 使用扩展的正则表达式( ERE ):

    • egrep(1) 匹配满足多个 pattern 的文本。

    • awk(1) 进行简单的文本处理。

    • tcl(3tcl) 可以进行任何你想得到的文本处理:参见 re_syntax(3) 。经常与 tk(3tk) 一起使用。

    • perl(1) 可以进行任何你想得到的文本处理。参见 perlre(1) 。

    • 带有 re 模块的 python(1) 可以进行任何你想得到的文本处理。参见“/usr/share/doc/python/html/index.html”。

如果你不确定这些命令究竟做了什么,请使用“man command” 来自己把它搞清楚吧。

[注意]注意

排序的顺序和表达式的范围取决于语言环境 。如果你想要获得一个命令的传统行为,可以在命令之前使用 “LANG=C” 让 C 语言环境代替 UTF-8(参见 第 1.5.2 节 ““$LANG”变量”第 8.4 节 “语言环境”)。

[注意]注意

Perl 正则表达式( perlre(1) )、Perl 兼容正则表达式(PCRE)Pythonre 模块提供的正则表达式与一般的 ERE 相比多了许多通用的扩展。

1.6.2. 正则表达式

正则表达式被使用在许多文本处理工具中。它们类似 shell 的通配符,但更加复杂和强大。

正则表达式描述要匹配的模式,它是由文本字符和元字符构成的。

元字符仅仅是带有特殊含义的字符。它们有两种主要的形式,BREERE ,使用哪种取决于上述的文本工具。

表 1.24. BRE 和 ERE 中的元字符

BREERE正则表达式的描述
\ . [ ] ^ $ \ . [ ] ^ $ 通用的元字符
+ \? ( ) { } |BRE 独有的“\”转义元字符
+ ? ( ) { } |ERE 独有的不需要“\”转义的元字符
cc匹配非元字符c
\c\c匹配一个字面意义上的字符 “c”,即使 “c” 本身是元字符
..匹配任意字符,包括换行符
^^字符串的开始位置
$$字符串的结束位置
\<\<单词的开始位置
\>\>单词的结束位置
[abc…][abc…]匹配在 “abc…” 中的任意字符
[^abc…][^abc…]匹配除了 “abc…” 中的任意字符
rr匹配零个或多个 “r
r+r+匹配一个或多个 “r
r\?r?匹配零个或一个 “r
r1|r2r1|r2匹配一个 “r1” 或 “r2
(r1|r2)(r1|r2)匹配一个 “r1” 或 “r2“ ,并作为括号内的正则表达式

emacs 中的正则表达式基本上是 BRE 但含有 ERE 中的元字符+” 和 “?” 。因此,在 emacs 中没必要使用 “\” 来转义它们。

grep(1) 可以使用正则表达式来进行文本搜索。

尝试下列例子

  1. $ egrep 'GNU.*LICENSE|Yoyodyne' /usr/share/common-licenses/GPL
  2. GNU GENERAL PUBLIC LICENSE
  3. GNU GENERAL PUBLIC LICENSE
  4. Yoyodyne, Inc., hereby disclaims all copyright interest in the program
[提示]提示

参见第 9.2.7 节 “有颜色输出的命令”

1.6.3. 替换表达式

对于替换表达式,一些字符有特殊的含义。

表 1.25. 替换表达式

替换表达式替换表达式替换的文本
&正则表达式所匹配的内容(在 emacs 中使用 \&
\n前 n 个括号的正则表达式匹配的内容( “n” 是数字)

对 Perl 替换字符串来说,应使用“$&”而非“&”,应使用“$n”而非“\n”。

尝试下列例子

  1. $ echo zzz1abc2efg3hij4 | \
  2. sed -e 's/\(1[a-z]*\)[0-9]*\(.*\)$/=&=/'
  3. zzz=1abc2efg3hij4=
  4. $ echo zzz1abc2efg3hij4 | \
  5. sed -e 's/\(1[a-z]*\)[0-9]*\(.*\)$/\2===\1/'
  6. zzzefg3hij4===1abc
  7. $ echo zzz1abc2efg3hij4 | \
  8. perl -pe 's/(1[a-z]*)[0-9]*(.*)$/$2===$1/'
  9. zzzefg3hij4===1abc
  10. $ echo zzz1abc2efg3hij4 | \
  11. perl -pe 's/(1[a-z]*)[0-9]*(.*)$/=$&=/'
  12. zzz=1abc2efg3hij4=

请特别注意这些括号正则表达式的格式,以及这些被匹配的文本在不同的工具中是如何被替换的。

这些正则表达式在一些编辑器中也可以用来移动光标和替换文本。

在 shell 命令行行末的反斜杠 “\” 会跳脱一个换行符(作为空白符),并将光标移动到下一行的行首。

请阅读所有相关手册来学习这些命令。

1.6.4. 正则表达式的全局替换

ed(1) 命令可以在 “file” 中将所有的 “FROM_REGEX” 替换成 “TO_TEXT” 。

  1. $ ed file <<EOF
  2. ,s/FROM_REGEX/TO_TEXT/g
  3. w
  4. q
  5. EOF

sed(1) 命令可以在 “file” 中将所有的 “FROM_REGEX” 替换成 “TO_TEXT” 。

  1. $ sed -i -e 's/FROM_REGEX/TO_TEXT/g' file

vim(1) 命令可以通过使用 ex(1) 命令在 “file” 中将所有的 “FROM_REGEX” 替换成 “TO_TEXT” 。

  1. $ vim '+%s/FROM_REGEX/TO_TEXT/gc' '+w' '+q' file
[提示]提示

上面的 “c” 标志可以确保在每次替换时都进行交互式的确认。

多个文件( “file1”,“file2” 和 “file3” )可以使用 vim(1) 或 perl(1) 通过正则表达式进行类似的处理。

  1. $ vim '+argdo %s/FROM_REGEX/TO_TEXT/ge|update' '+q' file1 file2 file3
[提示]提示

上面的 “e” 标志是为了防止 “No match” 错误中断替换。

  1. $ perl -i -p -e 's/FROM_REGEX/TO_TEXT/g;' file1 file2 file3

在 perl(1)例子中 , “-i“ 是在每一个目标文件的原处编辑,”-p“ 是表示循环所有给定的文件。

[提示]提示

使用参数 “-i.bak” 代替 “-i” ,可以在文件名后添加 “.bak” 再保存。对于复杂的替换,这使得从错误中恢复变得容易。

[注意]注意

ed(1) 和 vim(1) 使用 BRE ; perl(1) 使用 ERE

1.6.5. 从文本文件的表格中提取数据

下面有一个文本文件 “DPL” ,里面含有 2004 年以前 Debian 项目的领导者名字和起始日期,并以空格分隔。

  1. Ian Murdock August 1993
  2. Bruce Perens April 1996
  3. Ian Jackson January 1998
  4. Wichert Akkerman January 1999
  5. Ben Collins April 2001
  6. Bdale Garbee April 2002
  7. Martin Michlmayr March 2003
[提示]提示

参见 “Debian 简史” 获取最新的 Debian 领导阶层历史

Awk 经常被用来从这种类型的文件中提取数据。

尝试下列例子

  1. $ awk '{ print $3 }' <DPL # month started
  2. August
  3. April
  4. January
  5. January
  6. April
  7. April
  8. March
  9. $ awk '($1=="Ian") { print }' <DPL # DPL called Ian
  10. Ian Murdock August 1993
  11. Ian Jackson January 1998
  12. $ awk '($2=="Perens") { print $3,$4 }' <DPL # When Perens started
  13. April 1996

Shell (例如 Bash )也可以用来分析这种文件。

尝试下列例子

  1. $ while read first last month year; do
  2. echo $month
  3. done <DPL
  4. ... 第一个 AWK 例子的一些输出

内建命令 read 使用 “$IFS” (内部域分隔符)中的字符来将行分隔成多个单词。

如果你将 “$IFS” 改变为 “:” ,你可以很好地使用 shell 来分析 “/etc/passwd”。

  1. $ oldIFS="$IFS" # save old value
  2. $ IFS=':'
  3. $ while read user password uid gid rest_of_line; do
  4. if [ "$user" = "bozo" ]; then
  5. echo "$user's ID is $uid"
  6. fi
  7. done < /etc/passwd
  8. bozo's ID is 1000
  9. $ IFS="$oldIFS" # restore old value

(如果要用 Awk 做到相同的事,使用 “FS=':'” 来设置域分隔符。)

IFS 也被 shell 用来分割参数扩展、命令替换和算术扩展的结果。这不会出现在双引号或单引号中。 IFS 的默认值为 <空格>、<tab> 和<换行符>。

请谨慎使用 shell 的 IFS 技巧。当 shell 将脚本的一部分解释为对它的输入时,会发生一些奇怪的事。

  1. $ IFS=":," # use ":" and "," as IFS
  2. $ echo IFS=$IFS, IFS="$IFS" # echo is a Bash builtin
  3. IFS= , IFS=:,
  4. $ date -R # just a command output
  5. Sat, 23 Aug 2003 08:30:15 +0200
  6. $ echo $(date -R) # sub shell --> input to main shell
  7. Sat 23 Aug 2003 08 30 36 +0200
  8. $ unset IFS # reset IFS to the default
  9. $ echo $(date -R)
  10. Sat, 23 Aug 2003 08:30:50 +0200

1.6.6. 用于管道命令的小片段脚本

下面的脚本作为管道的一部分,可以做一些细致的事情。

表 1.26. 管道命令的小片段脚本列表

脚本片段(在一行内输入)命令效果
find /usr -print找出”/usr“下的所有文件
seq 1 100显示 1 到 100
| xargs -n 1 <command>把从管道过来的每一项作为参数,重复执行命令
| xargs -n 1 echo把从管道过来的,用空格隔开的项,分隔成多行
| xargs echo把从管道过来的所有行合并为一行
| grep -e <regex_pattern>从管道过来,包含有 <regex_pattern>的行,提取出来
| grep -v -e <regex_pattern>把从管道过来,不包含有 <regex_pattern>的行,提取出来
| cut -d: -f3 -把从管道过来,用 “:“分隔的第三列提取出来 (passwd 文件等。)
| awk ‘{ print $3 }’把用空格隔开的第三列提取出来
| awk -F’\t’ ‘{ print $3 }’把用 tab 键隔开的第三列提取出来
| col -bx删除退格键,扩展 tab 键为空格键
| expand -扩展 tab 键到空格键
| sort| uniq排序并删除重复行
| tr ‘A-Z’ ‘a-z’将大小字母转换为小写字母
| tr -d ‘\n’将多行连接为一行
| tr -d ‘\r’删除换行回车符
| sed ‘s/^/# /‘在每行行首增加一个”#“符
| sed ‘s/.ext//g’删除 “.ext
| sed -n -e 2p显示第二行
| head -n 2 -显示最前面两行
| tail -n 2 -显示最后两行

使用 find(1) 和 xargs(1),单行 shell 脚本能够在多个文件上循环使用,可以执行相当复杂的任务。参见 第 10.1.5 节 “查找文件的语法”第 9.3.9 节 “使用文件循环来重复一个命令”.

当使用 shell 交互模式变得太麻烦的时候,请考虑写一个 shell 脚本(参见 第 12.1 节 “Shell 脚本”).