第 1 章 GNU/Linux 教程

目录

1.1. 控制台基础
1.1.1. shell 提示符
1.1.2. GUI 下的 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 中的 虚拟文件系统
1.4. 类 Unix 工作环境基础
1.4.1. 登录 shell
1.4.2. 定制 bash
1.4.3. 特殊按键
1.4.4. 鼠标操作
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 系统中使用过一段时间的命令行工具,你可能已经掌握了这份文档中的内容。那请把它当做一个实战检验和回顾。

启动系统之后,如果你没有安装 GUI(例如GNOME 或者 KDE),那么你会看到字符登录界面。假设你的主机名为foo,那么登录提示符将如下所示。

如果你安装了一个 GUI 环境,那么你仍然能够用 Ctrl-Alt-F3 进入基于字符的登录提示符,同时你能通过 Ctrl-Alt-F2 回到 GUI 环境(更多详情请参阅下文 第 1.1.6 节 “虚拟控制台”)。

foo login:

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

[注意] 注意

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

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

Debian GNU/Linux 12 foo tty3

foo login: penguin
Password:

Linux foo 6.5.0-0.deb12.4-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.5.10-1~bpo12+1 (2023-11-23) 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.

Last login: Wed Dec 20 09:39:00 JST 2023 on tty3
foo:~$

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

如果你在安装 Debian 的过程中,安装了一个 GUI 环境,那么你在启动系统后将使用图形登录界面。输入你的用户名和密码可以登录到非特权用户帐号。使用 Tab 键(跳格键)可以在用户名和密码之间移动,也可以使用鼠标主要键点击。

要在 GUI(图形用户界面)环境下获得 shell 提示符,你必须启动一个x 终端模拟器程序,例如gnome-terminal(1)、rxvt(1)或xterm(1)。在 GNOME 桌面环境下,你可以按 超级键(Windows 键),在搜索提示里输入"terminal"来打开终端。

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

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

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

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

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

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

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

[警告] 警告

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

[注意] 注意

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

如果你的桌面菜单没有使用适当权限启动 GUI(图形用户界面)的自动化管理工具,你可以在终端模拟器(例如gnome-terminal(1)、rxvt(1)或xterm(1))中 root 的 shell 提示符下启动它。参见第 1.1.4 节 “root shell 提示符”第 7.8 节 “X 服务端连接”

[警告] 警告

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

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

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

如果你处于 GUI 环境中,你可以通过 Ctrl-Alt-F3 键前往字符控制台 3,也就是同时按下左 Ctrl 键左 Alt 键F3 键。你可以按下 Alt-F2 回到 GUI 环境,它一般运行在虚拟控制台 2。

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

# chvt 3

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

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

# shutdown -h now

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

# poweroff -i -f

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

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

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

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


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


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

# 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操作系统中,文件被组织到目录中。所有的文件和目录排放在以“/”为根的巨大的树里。叫它树是因为如果你画出文件系统,它看起来就像一棵树,但是它是颠倒过来的。

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

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

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

[提示] 提示

如果您需要识别文件树和物理实体之间的对应关系,请尝试不带参数运行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 成为该组的成员。你需要重启之后重新登录(或运行 “kill -TERM -1”)[1]以启用新的组配置。

[注意] 注意

或者,你可以通过添加一行 “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 组,就可以创建拨号 IP 来连接到那些受信任的点上,需使用的命令行工具包括 pppd(8)、pon(1)以及 poff(1)。

某些著名的由系统提供的组允许它们的成员不带 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)。

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


[注意] 注意

ctime 不是文件创建时间。

[注意] 注意

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

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

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

  • 在历史上的 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) 命令修改已存在文件的时间戳。

对于时间戳,在非英语区域(“fr_FR.UTF-8”),ls 命令输出本地化字符串。

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

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

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

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

$ umask 002
$ echo "Original Content" > 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 -> foo
1449840 -rw-rw-r-- 2 penguin penguin 17 Oct 16 21:42 foo
$ rm foo
$ echo "New Content" > 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 -> 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 " 及其子目录

  • " /usr/sbin " 和 " /usr/bin "

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

Debian 系统针对文本的鼠标操作混合 2 种风格,外加一些新的方法:

  • 传统的 Unix 鼠标操作方式:

    • 使用 3 个按钮(单击)

    • 使用主要键

    • 由 X 应用,如 xterm,以及文本应用在控制台中使用

  • 现代 GUI(图形用户界面)鼠标操作方式:

    • 使用 2 个按钮(拖动 + 单击)

    • 使用主要键和剪贴板

    • 用于现代的 GUI(图形用户界面)应用,比如 gnome-terminal


这里,主要键的选择会高亮文本范围。在终端程序内,使用 Shift-Ctrl-C 来代替,这样可以避免终止一个运行的程序。

在现代滚轮鼠标上的中央滚轮,被认为是中间键,并可以被当做中间键使用。在 2 键鼠标系统的情况下,同时按左键和右键就相当于按中间键。

为了在 Linux 字符控制台中使用鼠标,您需要让 gpm(8) 作为后台守护进程(daemon)运行。

在使用类 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 及其它不常用编码格式的文本。有个建议就是在 GUI(图形用户界面) 环境下使用 UTF-8 编码,并安装要求的程序和字体。编辑器里可以选择独立于 GUI(图形用户界面)环境的编码格式。关于多字节文本可以查阅参考文档。

最近的 vim(1) 用完全的 "nocompatible" 选项启动自己,进入到 普通 模式。[2]


请使用 "vimtutor" 程序来学习 vim,通过一个交互式的指导课程。

vim 程序基于 模式 输入的按键来改变它的行为。在 插入-模式和 替代-模式下,输入的按键大部分进入了缓冲区。移动光标大部分在 普通-模式下完成。交互选择在 可视-模式下完成。在普通-模式下输入 ":" ,改变它的 模式 进入到 Ex-模式。 Ex-接受命令。

[提示] 提示

Vim 和 Netrw 软件包可以一起使用。Netrw 同时支持在本地和网络读写文件,浏览目录。用 "vim ." (一个点作为参数)来尝试 Netrw,在":help netrw"读取它的文档。

vim 的高级配置,参见 第 9.2 节 “定制 vim”

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

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

表 1.17. 基本的 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 系统初始化,其中一些会被某些应用程序重新设定。

  • PAM(可插拔身份验证模块)系统的模块,比如 pam_env 模块,可以通过 /etc/pam.conf"、 "/etc/environment"和"/etc/default/locale"设置环境变量。

  • 显示管理器(例如gdm3)可以通过"~/.profile"给 GUI(图形用户界面)会话重新设定环境变量。

  • 用户特有的程序初始化时,可以重新设置在 "~/.profile"、"~/.bash_profile" 和 "~/.bashrc" 中设置的环境变量。

默认的语言环境是在 "$LANG" 环境变量中定义,它在安装的时候配置为 "LANG=xx_YY.UTF-8",或者在接下来的 GUI(图形用户界面)中配置,例如在 GNOME 中是,"设置" → "区域 & 语言" → "语言" / "格式"。

[注意] 注意

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

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



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

$ echo $LANG
en_US.UTF-8
$ date -u
Wed 19 May 2021 03:18:43 PM UTC
$ LANG=fr_FR.UTF-8 date -u
mer. 19 mai 2021 15:19:02 UTC

这里,date(1)程序执行时使用了不同的语言环境值。

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

$ LANG=fr_FR.UTF-8
$ date -u
mer. 19 mai 2021 15:19:24 UTC
[提示] 提示

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

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

让我们试着记住下面 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  # open files
$ cat <&3 >&4       # redirect stdin to 3, stdout to 4
$ exec 3<&- 4>&-    # close files
$ cat bar
Hello

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


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

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

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

[注意] 注意

排序的顺序和表达式的范围取决于语言环境。如果你想要获得一个命令的传统行为,可以使用 “LANG=C” 或 C.UTF-8 语言环境代替原来的 UTF-8 语言环境(参见 第 8.1 节 “语言环境”)。

[注意] 注意

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
... same output as the first Awk example

内建命令 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.4.9 节 “使用文件循环来重复一个命令”.

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



[1] 在现代环境下,通过GUI(图形用户界面)菜单注销登录,在这里不一定生效。

[2] 即使旧的 vim 也能够启动完全的 "nocompatible" 模式,通过使用 "-N" 选项启动。