1. 什么是 shell、bash 和命令行
1.1 shell
从结构和功能上,可以把 Unix/Linux 操作系统分成操作系统内核、shell、 库函数和应用软件几个层次。其中操作系统内核可称为严格意义上的操作系统,它控制硬件系统资源,实现基本的操作系统功能 (比如运行新程序、打开文件、读写文件、分配内存等),提供程序运行环境。内核通过系统调用为 shell、库函数、应用程序提供直接访问内核功能的接口,应用程序又可以通过 shell 和 库函数间接访问内核。如果比喻一下,内核好比一个积木式机器人的核心部分,系统调用就像是这个核心提供的标准通用插口, shell、库函数、应用程序仿佛是这个机器人的外观和功能部件,它们通过标准通用插口与内核连接,可以实现这个机器人的不同外观和功能,使用户能以不同方式操作这个机器人。从这个意义上讲,内核是处于核心地位的,一般不需要替换它,而 shell、库函数和应用程序是可以替换的,只要它们遵循系统调用的标准就行。
严格意义上的操作系统指的是操作系统内核,它提供了操作系统的基本功能。
广义的操作系统指的是操作系统内核 + shell + 库函数 + 应用程序,它提供了一个操作系统的完整内容、功能和感觉。
Linux 发行版: 不同的Linux内核、shell、库函数、应用程序的组合构成了 Linux 发行版。
shell 是运行在操作系统内核上的一个应用程序,从用户感觉上看,在操作系统出现图形用户接口(GUI) 之前, shell 是用户交互式访问并管理操作系统的唯一途径,它是以命令行终端的方式进行上述操作的。当打开一台 Linux 系统主机并进入文本界面模式时,用户一旦成功登录,就会进入登录 shell。通过 shell 可以运行系统命令和工具软件,管理文件和目录,操纵文本文件,查找和定位文件,查看系统软硬件信息、管理和查看磁盘等诸多功能。
1.2 bash
如上所述,Linux/Unix 系统中的 shell 是可以替换的, 有 sh、bash、ksh、csh、tcsh、zsh 等多种 shell, bash 就是其中一种。
bash 是 Bourne Again shell 的简写,它是 GNU (http://www.gnu.org/) 项目开发使用的 shell, 它继承、兼容并扩展了早期出现的 Unix shell: Bourne shell,它汇入了 Korn shell 与 C shell 用户交互好和编程性强的优点,提供了命令补全、命令编辑、命令历史列表、重定向、管道、分支、循环和数组等特性,具有强大的功能。在 GNU/Linux 操作系统中,bash 是标准 shell。在 Linux 系统中,bash 以一个系统命令的形式呈现,它就是 Linux 文件层次结构标准 ( Filesystem Hierachy Standard ) 中的 /usr 目录中的 bash 命令,其路径名为 /usr/bash。GNU/Linux 中的 /bin/sh 通常是 /usr/bin/bash 的软链接。
1.3 命令行
命令行通常有两层含义。第一层含义指命令行界面, 即运行 shell 程序呈现的命令提示符和输入、编辑、修改 Linux 命令和其参数的交互式界面;第二层含义指命令行命令,即通过命令行界面输入并执行的 Linux 命令。
在 Linux 操作系统中,所有管理工具和应用软件都可以通过命令行运行(在命令行交互式界面中输入对应的命令)。
1.3.1 命令行参数和选项
在 Linux 操作系统的 bash 英文手册页中,对于命令行参数和选项使用了三个术语: option, argument 和 parameter。其中 option 翻译成中文是 "选项" 的意思,而 argument 和 parameter 翻译成中文都是 "参数" 的意思,然此 "参数" 非彼 "参数",二者有所区别。
arguments
在 bash 的交互式命令行界面,通常含有以下内容:
命令提示符 Linux命令 argument1 [argument2 [argument3] [ ...]]
对于普通用户,命令提示符一般是 $
字符,对于 root 用户,一般是 #
,它可以通过修改 PS1
环境变量进行设定。
Linux 命令就是像 ls, mv, find 这样的命令。
argument1 [argument2 [argument3] [ ...]] 被称为 argument 列表,英文称为 arguments,它是一个由未转义空格分隔的词(word)列表,每个词就是一个 argument。词的含义是以未转义空格为边界的字符串,如果想在词中使用空格字面值,就得在词中嵌入用转义符号转义的空格(w\ ord,这是一个词),或者用引号把含有空格的词引起来('w ord',和前者相同)。
options
bash 命令通常含有 options(选项), 用于使命令按选项规定的特定要求运行。有些选项带有参数 ( parameters )。选项的参数用于精细控制 Linux 命令在该选项下运行时的行为。选项有短选项和长选项两种,短选项以短划线(-)后面跟一个字符表示,长选项以双短划线(--)后面跟一个字符串表示。短选项的形式是从 Unix 继承下来的,长选项形式��� GNU 所特有的。有些选项既提供了短选项形式,同时又提供了长选项形式。短选项可以组合起来使用,即以短划线开头,后面跟几个选项字符,表示开启了这些选项,其中最后一个选项字符后面可以带有参数列表 ( parameters ),这些参数仅是最后一个选项字符的参数。
parameters
选项可以带有参数列表 (parameters)。有时把选项和它的参数列表作为一个整体称为选项。一个选项和它的 parameter 可以构成一个 arguemnt, 所有选项和其参数 ( parameters ) 共同构成了 arguments。除了这种 arguments 之外,其他 arguments 也被称为 parameter, 这些 parameter 将按照顺序被传递给环境变量 $0
、$1
...
例子
我们举几个例子结束对 options、arguments和 parameters的讨论:
$ ls -l # $ 是命令行提示符
# ls 是 Linux 命令
# -l 是 ls 的短选项,同时也是一个 argument,但它没有 parameter
$ ls -l --color
# --color 是一个长选项
# -l 和 --color 都是 argument
$ ls -llh # 把短选项组合在一起
$ cut -d ';' source.txt
# cut 是 linux 命令
# -d ';' 是选项整体, ';' 是选项 -d 的参数(parameter)
# source.txt 是选项整体之外的参数(parameter)
# -d ';'、 source.txt 二者都是 argument
1.3.2 -字符是一个特殊的arguemnt
命令行中的 - 字符是一个特殊的 arguemnt,如果命令接受文件名作为参数,或者命令的选项接受文件作为参数,则 - 字符可以用来表示标准输入或标准输出。两个 - 字符连用标志命令的所有选项结束,-- 后面的所有 arguments 被用作命令的位置参数。
$ cat - # - 表示标准输入,cat 命令把来自标准输入的内容输出到标准输出
$ mkinitramfs -o - # -o 选项的参数是打包后的文件名, 用 - 作为参数表示打包到标准输出
$ rm -- -file-to-delete # -- 表示 rm 没有选项
# -file-to-delete 被用作 rm 的参数 (parameter),
# 即,它是 rm 要删除的文件名
# 如果不用 -- 表示结束选项,开始参数列表,
# 则 -file-to-delete 被视为 rm 的无效选项
cd 命令中的 - 字符表示 cd 命令访问过的目录栈中的最后一个目录的上一个目录。举个例子说明一下:
$ cd absolutePath1 # absolutePath1 是绝对路径名
$ cd absolutePath2 # absolutePath2 是绝对路径名
$ cd - # 相当于 cd absolutePath1
2. 文件重定向
在 bash 命令行中常使用文件重定向,该功能常常能够简化操作。
2.1 文件描述符和标准文件
Linux/Unix 系统中有文件描述符的概念,打开文件或创建新文件时,都会给文件分配一个文件描述符。文件描述符是一个从 0 开始计数的正整数,Linux/Unix 的系统调用使用文件描述符操纵文件,在一个系统中,文件描述符是可以被回收重新利用的,当关闭一个已打开的文件时,将会释放文件描述符,系统中维护一个可用文件描述符的 "池",每次打开或创建文件时,将从这个 "池" 中选择最小的那个文件描述符分配给新打开或创建的文件。
Linux/Unix 系统约定把文件描述符 0 分配给标准输入,1 分配给标准输出,2 分配给标准错误。通常可以把标准输入理解为键盘,把标准输出理解为显示器,默认情况下标准错误和标准输出使用同样的设备。
2.2 文件重定向
命令行工具常常默认使用标准输入作为输入,标准输出作为输出,标准错误作为错误输出,这是在编写其源代码时就已经确定了的。所谓文件重定向,就是输入输出时把这三个标准文件的文件描述符临时指向其他文件,把本来读写标准文件变为读写这些其他文件。由于文件描述符的指向发生了改变,所以称为重定向。例如:
$ cat <test.txt >o.txt # < 表示重定向标准输入
# > 表示重定向标准输出
# 从 test.txt 中读取内容,写至 o.txt
$ cat test1.txt > result.txt
$ cat test2.txt >> result.txt # >> 表示重定向标准输出,并在 result.txt 的尾部添加 test2.txt
重定向时可以用表示文件描述符的数字指定重定向哪个文件描述符:
$ find / -type f -iname *ramfs* 2>/dev/null # 把标准错误(错误消息)重定向到 /dev/null(黑洞设备)
3. 管道
管道表示把管道符号(|) 前的命令的标准输出用作管道符号后面的命令的标准输入。我们用有方向的水处理器类比一个Linux命令,水处理器的入口就是标准输入,水处理器的出口就是标准输出,管道符号就像一个连接器,把两个水处理器连接起来,上一个水处理器的标准输出连接到下一个水处理器的标准输入。这样,两个水处理器可能进行了不同的水处理,但水仍在管道中连续的流动。我们发现,像水处理一样,我们的 Linux 命令可以对数据(通常是文本数据)进行不同的处理,但通过管道,可以把上一个命令的处理结果发送给下一个命令作为其输入,当我们需要对数据进行多种处理时以得到最终的结果时,这样做非常有用。举例如下:
$ cat test.txt
12cd
$ cat test.txt | tr '1-9' 'a-i' # tr 命令的作用是按次序把 1-9 的数字替换成 a-i
abcd # (a-i 正好是9个字符,与 1-9 对应)
3.1 双头管道命令 tee
tee 是一个特殊的管道,它有一个输入,两个输出,输入是标准输入,一个输出是标准输出,另一个输出是 tee 命令指定的文件。举例如下:
$ cat test.txt | tee tmp.txt # 在标准输出上输出 test.txt,同时把它保存在 tmp.txt 中。
4. 命令行编辑
在命令行中编写命令时,直到按下 Enter 键之前,都不会执行命令,这时,可以通过所谓的行编辑模式对命令行进行编辑。命令行编辑一般有以下几点需要注意:
- 左右方向键可以在命令行中左右移动光标
- 按下 Backspace(退格键) 可以删除光标前的字符
- 按下任何一个字符键,可以在当前光标位置写入字符
- 按下 \ 字符可以使命令行延续到下一行
- 按下回车键可以运行命令
5. 命令行历史与补全
5.1 命令行历史
命令行历史由 history 命令实现。默认情况下,它可以记录最近使用的 500 条命令行命令,这个值由 HISTSIZE 环境变量决定。在命令行中键入 history,即可显示出最近的 500 条命令,每条命令前面都有一个表示命令序号的数字,使用 ! 加上数字,即可调用对应的历史命令。
5.2 命令行补全
命令行补全是提高 Linux 命令行输入效率、减少输入错误率的一个功能。每当在命令行中键入一个新的字符,按下 TAB 键时,bash 将会显示所有使用已键入的字符开头的可用的命令行命令,这些命令被称为候选命令。当候选命令只有一个时,TAB 键会补全这个命令,就像你通过键盘完全键入了该命令一样。当候选命令是一个列表时,用户可以在这些命令中选择自己的目标命令使用,如果目标命令不在这个列表中,可以使用退格键删除已键入字符并重新键入新的字符,直到目标命令出现在候选命令列表中为止。
6. inode 节点
inode 是一个与文件系统关联的概念,它有以下几种含义: 首先,它是一个数据结构,在系统的 C 语言接口中是一个结构体,含有档案及目录的基本信息,如时间、文件名、用户、群组及权限等;其次,在某些文件系统如 ExtX (X:2~4) 中,它是存放在磁盘中的索引节点信息块,其内容是按照上述数据结构组织起来的,每个文件不论其占用多少磁盘块,都对应一个 inode 节点; 最后,VFS (Vitual Filesystem Switch,称为虚拟文件系统或虚拟文件系统转换) 在系统内存中的映像中也存在 inode 的身影,它联结了运行中的操作系统内核和文件系统,对于没有物理 inode 节点信息块的文件系统,操作系统通过设备驱动程序等抽象提取出文件的 inode 结构,映射到 VFS 中的 inode 内存映像中。
在磁盘上创建 ExtX 这样的文件系统时,会在磁盘上生成空闲的 inode 节点。每创建一个文件,都为其分配一个 inode 节点,当 inode 节点耗尽时,即使仍然有空闲的磁盘块,也不能在该磁盘上创建新的文件了。
bash 的 ls -i 命令可以查看 inode 节点的信息。
7. 软链接和硬链接
磁盘上的文件可以有 "分身",这些 "分身" 都指向原始文件,而不是原始文件的副本;当通过这些 "分身" 编辑和修改文件时,改动发生在原始文件上。在 Linux 系统上,这些文件的 "分身" 称为链接。
链接分为硬链接和软链接,硬链接相当于指向原始文件的 inode 的指针,也就是原始文件的一个 "别名",硬链接不占用磁盘空间,每当新建一个硬链接,通过 ls 显示的文件的链接数都会加1,删除一个硬链接时则相反。使用 ls 显示同一个文件的各个硬链接的 inode 时,其值相同。可以把原始文件也看作一个硬链接,它把该文件占据的磁盘内容指向该文件的 inode,当删除最后一个硬链接时,文件的链接数归零,相当于删除了该文件。软链接也指向原始文件,它其实是磁盘上的一个文本文件,文件中含有指向原始文件的位置信息。软链接从本质上是独立于原始文件的另一个文件,具有和原始文件不同的 inode 节点,它占用磁盘空间,但是通常比原始文件占用量少得多。创建原始文件的软链接不增加其链接数,删除软链接也不减少原始文件的链接数。
由于硬链接可被理解为指向原始文件的 inode 的指针,所以只能在同一个文件系统上创建硬链接,因为每个文件系统都维护自己的 inode 列表,不同文件系统间可能会有重复的 inode。
可以跨分区、跨设备创建文件的软链接。
$ ln source_file hard_line # 生成硬链接
$ ln -s source_file soft_link # 生成软链接
8. 转义字符
bash 中很多地方都用到了转义字符,譬如 echo/printf 命令。转义字符的形式是反斜线+ASCII字符,当这个组合有系统规定的含义(转义含义)时,就使用它的转义含义,对于普通字符,其转义形式仍为普通字符。譬如,'\a' 表示响铃符,因为 '\a' 组合起来具有这个含义;而 '\c' 就是普通字符 'c',因为这个组合没有特殊含义。
具有下列常用的转义字符:
\a 响铃符 (bell)
\b 退格符
\e
\E 转义字符
\f 进纸符
\n 换行符
\r 回车符
\t 水平制表符
\v 垂直制表符
\\ 反斜线
\' 单引号
\" 双引号
\? 问号
\nnn 值为八进制值 nnn 的 8比特字符(1到3位八进制数)
\xHH 值为十六进制值 HH 的 8比特字符(1到2位十六进制数)
\uHHHH 值为十六进制值 HHHH 的 Unicode (ISO/IEC 10646) 字符(1到4位十六进制数)
\UHHHHHHHH 值为十六进制值 HHHHHHHH 的 Unicode (ISO/IEC 10646) 字符(1到8位十六进制数)
\cx control-x 字符
8. 文件类型
Linux 中有以下文件类型: 普通文件、目录、块设备文件、字符设备文件、套接字、管道和链接。
普通文件又包括纯文本文件(以纯文本格式存储的文件,如普通纯文本文件、源代码文件、脚本文件等)、二进制文件(可执行程序和链接库)、数据文件(如字处理软件编辑存储的文件、数据库软件的数据文件等)和压缩文件(如 gzip、bzip等压缩工具的压缩包 )等。可以通过 cat/vim/Emacs 等工具显示或编辑文本文件的内容,可以通过 od 命令显示二进制文件的内容。
目录也是一个文件,它有特定的目录文件格式,其内容是该目录中的文件及其路径和文件属性,ls 命令可以查看目录文件的内容。
Linux 系统中的设备被抽象为文件提供给自身和用户使用,分为块设备文件和字符设备文件两类,块设备可以一次读写一块(多个)字节,字符设备一次只能读写一个字节。
通过网络传输消息时使用套接字文件。
管道文件是进程间通信的一种方法。
链接相当于 Windows 下的快捷方式,允许在 Linux 系统中的不同路径中以别名(不多占文件系统空间地)使用同一份原始文件。
通过 ls -l
命令可以查看文件类型,file
命令可以查看文件类型的详细信息,stat
命令可以查看文件的状态。
9. 通配符
在 Linux 命令行中可以使用如下两个通配符: *
, ?
。"通配符" 这个词的含义指这两个字符是可以代替文件名中可以出现的任何其他字符的字符,其中 *
可以代替 0 或任意多个其他字符,?
只能代替 1 个字符。如:
$ ls a* # 列出当前目录下名字以a开头的任何文件
$ ls a? # 列出当前目录下名字以a开头,后面跟一个字符的任何文件
10. 正则表达式
grep、sed 和 awk 这三个工具软件的命令行中,可以使用正则表达式。所谓正则表达式,是一些精心构造的字符串,按照预先定义的规则,在这些字符串中使用类似通配符的特定字符匹配一组字符。在受到支持的工具软件中,可以用正则表达式实现字符串的模糊查找和替换功能。编写这类工具软件的源代码时,加入了对正则表达式的支持。Linux 命令行中,通常支持两种正则表达式语法--基本正则表达式和扩展的正则表达式,后者是对前者的扩展。工具软件可以使用命令行选项作为开关,开启对扩展的正则表达式的支持 (譬如,在 grep 的命令行中,使用 grep -e )。
# 基本正则表达式的语法
. 匹配任意单个字符
[] 匹配指定范围内的任意单个字符;
[^] 匹配指定范围外任意单个字符
字符类 [:digit:] [:lower:] [:upper:] [:alpha:] [:alnum:] [:space:] 分别匹配数字、小写字母、大写字母、字母、字母数字和空白字符
匹配次数 用在要指定其出现的次数的字符后面,用于限制其前面字符出现的次数
* 匹配其在前面的字符任意次:0,1..多次
.* 匹配任意长度的任意字符
\? 匹配其前面字符0次或一次,即前面字符是可有可无
\+ 匹配其前面字符一次或是多次,至少一次,
\{m\} 匹配前面字符m次
\{m,n\} 匹配其前面的字符至少m次,最多n次
\{0,n\} 最多n次
\{m,\} 最少m次
位置锚定:
^ 匹配行首,即每行的最左侧
$ 匹配行尾,即每行的最右侧
^PATTERN 用于匹配PATTERN的最左侧
^$ 空白行
^[[:space:]] 空行或开头为空白字符的行
单词:非特殊字符组成的连续字符(字符串)都成为单词
\< 或 \b 匹配词首,即每个单词的最左侧
\> 或 \b 匹配词尾,即每个单词的最右侧
\<PATTERN\> 匹配完整单词
11. 通用选项
下列选项在多数 Linux 命令中都可用,且其含义通常保持不变,如下所示。
-a 显示所有对象
-c 生成一个计数
-d 指定一个目录
-e 扩展一个对象
-f 指定读入数据的文件
-h 显示命令的帮助信息
-i 忽略文本大小写
-l 产生输出的长格式版本
-n 使用非交互模式(批量)
-o 指定讲所有输出重定向到的输出文件
-q 以安静模式运行
-r 递归地处理目录和文件
-s 以安静模式运行
-v 生成详细输出
-x 排除某个对象
-y 对所有问题回答 yes