第 1 章 GNU/Linux 教程

目录

1.1. 控制台基础
1.1.1. shell 提示符
1.1.2. X 系统下的 shell 提示符
1.1.3. root 账户
1.1.4. root shell 提示符
1.1.5. GUI 系统管理工具
1.1.6. 虚拟控制台
1.1.7. 怎样退出命令行提示符
1.1.8. 怎样关闭系统
1.1.9. 恢复一个正常的控制台
1.1.10. 建议新手的额外软件包
1.1.11. 额外用户账号
1.1.12. sudo 配置
1.1.13. 玩的时间
1.2. 类 Unix 文件系统
1.2.1. Unix 文件基础
1.2.2. 文件系统深入解析
1.2.3. 文件系统权限
1.2.4. 控制新建文件的权限:umask
1.2.5. 一组用户的权限(组)
1.2.6. 时间戳
1.2.7. 链接
1.2.8. 命名管道(先进先出)
1.2.9. 套接字
1.2.10. 设备文件
1.2.11. 特殊设备文件
1.2.12. procfs 和 sysfs
1.2.13. tmpfs
1.3. Midnight Commander (MC)
1.3.1. 自定义 MC
1.3.2. 启动 MC
1.3.3. MC 文件管理
1.3.4. MC 命令行技巧
1.3.5. MC 内部编辑器
1.3.6. MC 内部查看器
1.3.7. 自动启动 MC
1.3.8. MC 中的 FTP 虚拟文件系统
1.4. 类 Unix 工作环境基础
1.4.1. 登录 shell
1.4.2. 定制bash
1.4.3. 特殊按键
1.4.4. Unix类型的鼠标操作
1.4.5. 分页程序
1.4.6. 文本编辑器
1.4.7. 设置默认文本编辑器
1.4.8. 定制vim
1.4.9. 记录 shell 活动
1.4.10. 基本的Unix命令
1.5. 简单 shell 命令
1.5.1. 命令执行和环境变量
1.5.2. “$LANG”变量
1.5.3. "$PATH" 变量
1.5.4. "$HOME" 变量
1.5.5. 命令行选项
1.5.6. Shell 通配符
1.5.7. 命令的返回值
1.5.8. 典型的顺序命令和 shell 重定向
1.5.9. 命令别名
1.6. 类 Unix 的文本处理
1.6.1. Unix 文本工具
1.6.2. 正则表达式
1.6.3. 替换表达式
1.6.4. 正则表达式的全局替换
1.6.5. 从文本文件的表格中提取数据
1.6.6. 用于管道命令的小片段脚本

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

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

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

[注意] 注意

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

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

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)来开始,同时显示一个命令提示符。

Debian GNU/Linux jessie/sid foo tty1
foo login: penguin
Password:
Last login: Mon Sep 23 19:36:44 JST 2013 on tty3
Linux snoopy 3.11-1-amd64 #1 SMP Debian 3.11.6-2 (2013-11-01) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
foo:~$

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

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

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

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

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

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

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

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

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

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

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

[警告] 警告

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

[注意] 注意

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

如果你的桌面菜单没有适当的权限启动系统管理工具,你可以在 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 屏幕。

在默认的 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。

# chvt 1

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

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

# shutdown -h now

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

# 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 关闭远程系统”

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

# apt-get update
 ...
# apt-get install mc vim sudo
 ...

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


阅读一些信息文档,也是一个好的主意。


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

# apt-get install package_name

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

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

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

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

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

[警告] 警告

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

[小心] 小心

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

[小心] 小心

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

[注意] 注意

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

[注意] 注意

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

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

These files and directories can be spread out over several devices. mount(8) serves to attach the filesystem found on some device to the big file tree. Conversely, umount(8) detaches it again. On recent Linux kernels, mount(8) with some options can bind part of a file tree somewhere else or can mount filesystem as shared, private, slave, or unbindable. Supported mount options for each filesystem are available in "/usr/share/doc/linux-doc-*/Documentation/filesystems/".

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

下面是一些 Unix 文件基础。

[注意] 注意

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

[注意] 注意

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

[注意] 注意

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

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


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

Each file, directory, named pipe (a way two programs can share data), or physical device on a Debian GNU/Linux system has a data structure called an inode which describes its associated attributes such as the user who owns it (owner), the group that it belongs to, the time last accessed, etc. The idea of representing just about everything in the filesystem was a Unix innovation, and modern Linux kernels have developed this idea ever further. Now, even information about processes running in the computer can be found in the filesystem.

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

[提示] 提示

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

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

  • 拥有这个文件的用户u

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • 链接到文件的硬链接数

  • 文件所有者的用户

  • 这个文件所属的

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

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

  • 文件的名字


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

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

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

# cd /some/location/
# chown -R foo:bar .
# 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”这样所有人可写或同组可写的目录下文件内容的安全,不仅要去除可写权限,还要给其所在目录设置粘滞位。否则,该文件可以被任意对其所在目录有写权限的用户删除并创建一个同名的新文件。

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

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

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


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

尝试下列例子

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

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

为了使组权限应用到一个特定用户,这个用户需要通过使用 “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 权限访问某些特定的文件和设备。


[提示] 提示

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

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


由系统提供的用户和组的完整列表,参见由 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)

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

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

$ umask 002
$ echo "Original Content" > 1 foo
$ ls -li foo
1449840 -rw-rw-r-- 1 penguin penguin 17 Oct 16 21:42 foo
$ ln foo bar     # hard link
$ ln -s foo baz  # symlink
$ ls -li foo bar baz
1449840 -rw-rw-r-- 2 penguin penguin 17 Oct 16 21:42 bar
1450180 lrwxrwxrwx 1 penguin penguin  3 Oct 16 21:47 baz -> 2 foo
1449840 -rw-rw-r-- 2 penguin penguin 17 Oct 16 21:42 foo
$ rm foo
$ echo "New Content" > 3 foo
$ ls -li foo bar baz
1449840 -rw-rw-r-- 1 penguin penguin 17 Oct 16 21:42 bar
1450180 lrwxrwxrwx 1 penguin penguin  3 Oct 16 21:47 baz -> 4 foo
1450183 -rw-rw-r-- 1 penguin penguin 12 Oct 16 21:48 foo
$ cat bar
Original Content
$ cat baz
New Content

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

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

[小心] 小心

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

[注意] 注意

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

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

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

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

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

尝试下列例子

$ cd; mkfifo mypipe
$ echo "hello" >mypipe & # put into background
[1] 8022
$ ls -l mypipe
prw-rw-r-- 1 penguin penguin 0 Oct 16 21:49 mypipe
$ cat mypipe
hello
[1]+  Done                    echo "hello" >mypipe
$ ls mypipe
mypipe
$ rm mypipe

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

设备文件有两种类型。

  • 字符设备

    • 每次访问一个字符

    • 一个字符等于一个字节

    • 如键盘、串口…

  • 块设备

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

    • 一个块>一个字节

    • 如硬盘等…

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

[注意] 注意

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

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

$ ls -l /dev/sda /dev/sr0 /dev/ttyS0 /dev/zero
brw-rw---T  1 root disk     8,  0 Oct 16 20:57 /dev/sda
brw-rw---T+ 1 root cdrom   11,  0 Oct 16 21:53 /dev/sr0
crw-rw---T  1 root dialout  4, 64 Oct 16 20:57 /dev/ttyS0
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()机制填充。

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-*软件包提供。

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-*提供。

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

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

$ sudo apt-get install mc

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

  • "/etc" 及其子目录

  • " /var/log " 及其子目录

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

  • " /sbin " 和 " /bin "

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

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

我认为习惯于使用 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 环境的编码格式。关于多字节文本可以查阅参考文档。

让我们来学习基本的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)

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

尝试下列例子

$ man man
$ man bash
$ man builtins
$ man grep
$ man ls

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

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

$ <commandname> --help
$ <commandname> -h

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

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

  1. 设置变量值(可选)

  2. 命令名

  3. 参数(可选)

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

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

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

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

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

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

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


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

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

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

[注意] 注意

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


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

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

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

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

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

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

$ export LANG
[注意] 注意

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

[提示] 提示

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

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

[注意] 注意

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

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


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

尝试下列例子

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

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

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

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

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


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

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

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

[注意] 注意

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

[注意] 注意

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

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

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

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


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

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

尝试下列例子

$ egrep 'GNU.*LICENSE|Yoyodyne' /usr/share/common-licenses/GPL
GNU GENERAL PUBLIC LICENSE
GNU GENERAL PUBLIC LICENSE
Yoyodyne, Inc., hereby disclaims all copyright interest in the program

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

Ian     Murdock   August  1993
Bruce   Perens    April   1996
Ian     Jackson   January 1998
Wichert Akkerman  January 1999
Ben     Collins   April   2001
Bdale   Garbee    April   2002
Martin  Michlmayr March   2003
[提示] 提示

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

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

尝试下列例子

$ awk '{ print $3 }' <DPL                   # month started
August
April
January
January
April
April
March
$ awk '($1=="Ian") { print }' <DPL          # DPL called Ian
Ian     Murdock   August  1993
Ian     Jackson   January 1998
$ awk '($2=="Perens") { print $3,$4 }' <DPL # When Perens started
April 1996

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

尝试下列例子

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

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

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

$ oldIFS="$IFS"   # save old value
$ IFS=':'
$ while read user password uid gid rest_of_line; do
    if [ "$user" = "bozo" ]; then
      echo "$user's ID is $uid"
    fi
  done < /etc/passwd
bozo's ID is 1000
$ IFS="$oldIFS"   # restore old value

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

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

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

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

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


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

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