在Unix/Linux操作系统中,shell俗称壳,区别于kernel内核。shell有双重功能,一方面,shell是一个命令解释器,用于处理shell内置的命令或者用户自定义的命令,可以在shell终端与用户交互,等待用户输入一个命令或者一串命令,然后以用户指定的方式执行命令;另一方面,shell是一种程序设计语言,更确切地说是一种脚本语言,一种解释型语言,类似于Python,有内置的环境变量和函数,也可以由用户自定义变量和函数,程序自上而下执行,我们可以在Linux系统或者其它很多的开源代码中找到shell脚本的应用。
历史上,shell有很多版本,这里简单介绍几个主要的版本。早期是AT&T的Bell实验室随Unix V7引入的Bourne Shell,以Stephen Bourne的名字命名,使用ALGOL风格,运行为login shell模式(另外一种模式是non-login shell)。随之,为了让用户更好地使用交互式功能,C Shell出现了,由Berkeley学校开发,使用C语言风格,新增了一些实用的命令。后来,AT&T Bell的David Korn组织开发了Korn Shell,即K Shell,结合了Bourne Shell的语法和C Shell的交互式特性,增加了更多的功能,广受用户欢迎。1983年,Richard Stallman发起了GNU计划,目的是创建一套完全自由的操作系统,Bash随之诞生,全名即Bourne Again SHell,是大多数Linux系统的默认shell,也是使用最广的shell。
关于GNU Bash的详细介绍和参考手册可查看如下官网:
http://www.gnu.org/software/bash/
https://www.gnu.org/software/bash/manual/bashref.html
下面介绍Bash的几个常识。
标准——
Bash遵循IEEE POSIX标准,IEEE即Institute of Electrical and Electronics Engineers,电气与电子工程师协会,POSIX即Portable Operating System Interface,可移植的操作系统接口。
功能——
类似于其它程序设计语言,Bash也有自己的内置函数/命令和变量,可以自定义函数和变量,支持一定的运算符,包括普通运算符和重定向运算符。
命名——
Bash中的变量名可以是字母、数字、下划线,但首字母不能是数字,而且自定义的变量名要避免使用Bash内置的变量,以免产生冲突。
状态——
Bash命令有自己的退出/返回状态,就像其它程序设计语言的函数返回值一样,这个退出状态的最小值为0,最大值为255,也就是一个无符号字节(八个二进制位)的取值范围,一般情况下,状态码为0表示命令执行成功。对于一个简单的内置命令来说,其退出状态是POSIX中waitpid函数规定的退出状态,如果命令执行期间被信号n中断,退出状态为”128+n“。如果命令未找到,退出状态为127,找到了却不可执行,退出状态为126,内建命令用法不正确时退出状态为2。
后缀——
shell脚本的后缀一般是“.sh”,但这个后缀并不是必需的,添加后缀的主要目的是与其它的文件进行区分,给我们一个直观的认识,shell脚本可以是任意的后缀或者不使用后缀。
注释——
在shell脚本中,“#”符号是个特殊字符,表示注释,类似于其它程序设计语言的注释符,脚本执行时,这个注释符及后面的内容会被忽略。在shell命令行(交互式shell)中,“#”也可以作为注释符,前提是shell支持这样的操作,是否支持我们可以使用shell内建命令shopt查看interactive_comments的状态,在shell命令行输入“shopt interactive_comments”,输出“interactive_comments on”表示支持,输出“interactive_comments off”表示不支持,如果想激活这个状态执行命令“shopt -s interactive_comments”,关闭这个状态执行命令“shopt -u interactive_comments”。
引用——
引用即quoting,不同于其它语言如c++的引用reference,在shell中,引用的作用是去除某些字符或变量的特殊含义,保留其字面含义。引用有三种用法,转义字符、单引号和双引号。转义字符即反斜线“\”,用来转义下一个特殊字符为其字面含义,当转义字符出现在行尾时又称为续行符,表示这一行还没有结束,还包括下一行的内容。单引号(”)中的字符串能保留各个字符的字面含义,单引号要成对使用,引号对内不能有别的单个单引号,即使是转义过的单引号也不行,但可以有单个双引号。双引号(”“)中的字符串也能保留各个字符的字面含义,双引号也要成对使用,引号对内虽不能有别的单个双引号,但可以有转义过的双引号,还可以有单个单引号。在双引号中,美元符号“$”保留其特殊功能,反单引号“`”(键盘上与波浪线一个位置)保留其特殊功能,如果打开历史扩展的话感叹号“!”保留其特殊功能,对于转义字符,如果其后面是反斜线、双引号、美元符号、反单引号,转义符号也将保留其转义功能。
转义——
shell中转义字符用法特殊,这里着重介绍一下。在某些场合下,比如说正则表达式,因为有许多字符是特殊字符,有着特殊含义,所以在正则匹配时要对这些特殊字符进行转义,转义成它们的字面意思。对于换行符,不同的操作系统有不同的格式,Linux上为”\n“,Mac上位”\r“, Windows上为”\n\r“。有时候,对一个字符串引用时要使用单引号而非双引号,比如说awk这个命令,字符串中若包含转义字符,会根据一定的规则进行处理,下面是ANSI(即American National Standards Institute美国国家标准学会) C标准定义的转义字符:
\a alert (bell)
\b backspace
\e
\E an escape character
\f form feed
\n new line
\r carriage return
\t horizontal tab
\v vertical tab
\\ backslash
\' single quote
\" double quote
\nnn the eight-bit character whose value is the octal value nnn (one to three digits)
\xHH the eight-bit character whose value is the hexadecimal value HH (one or two hex digits)
\uHHHH the Unicode (ISO/IEC 10646) character whose value is the hexadecimal value HHHH (one to four hex digits)
\UHHHHHHHH
the Unicode (ISO/IEC 10646) character whose value is the hexadecimal value HHHHHHHH (one to eight hex digits)
\cx a control-x character
先介绍下执行简单的shell命令的方法。在shell终端命令行直接输入shell命令,然后按下回车键,比如说执行打印系统信息的命令“uname”,将会显示操作系统的内核名字“Linux”。
$ uname
Linux
有些shell命令支持输入参数,在命令与参数或参数与参数间输入空格进行区分,比如说上面提到的命令“uname”就支持一些参数,如参数“-p”,用于打印处理器的类型,效果如下。
$ uname -p
x86_64
shell命令如果不是以斜线“/”(绝对路径)开头的,这个命令有一定的查找顺序。首先查找这个命令是否为一个函数,不是的话再查找这个命令是否为内建命令,最后在环境变量PATH所代表的各目录下搜索这个命令,最后一步操作是通过哈希hash完成的。
执行shell脚本时有两点需要注意。
第一点是是否声明了执行脚本的解释器。因为在shell环境中可以执行的脚本不只shell脚本,还包括其它类型的脚本,比如说python脚本、node脚本等,所以需要声明脚本执行由什么解释器来完成。由于在shell环境中的默认解释器是bash,所以对于一个shell脚本来说,不声明执行脚本的解释器也是可以的。如何声明执行脚本的解释器,有两种方法。一种是在脚本开头指定解释器的绝对路径,这个路径可以使用命令“which”查看,对于bash来说,执行命令“which bash”,结果即为bash的绝对路径。有时候可能有多个路径,选择合适的一个即可。如果是“/bin/bash”,那么在shell脚本的开头声明双引号内的内容“#!/bin/bash”即可,也就是一个井号,一个感叹号,后面接着路径。另一种是使用环境变量“env”,让环境变量去查找解释器的具体路径。同样需要声明env的绝对路径,通过命令“which env”得知其绝对路径为“/usr/bin/env”,然后在shell脚本的开头声明双引号的内容“#!/usr/bin/env bash”即可,也就是一个井号,一个感叹号,后面接着路径,最后是空格和解释器的名字bash。
第二点是脚本是否有可执行权限。因为要执行脚本,所以执行这个脚本的角色要有可执行权限。一般情况下,新建一个文档,如touch、vim、gedit、sublime、nautilus等方式建立的脚本,都是没有可执行权限的。添加或去掉可执行权限可使用命令“chmod”完成,添加可执行权限的命令为“chmod +x your_script_name“,去掉可执行权限的命令为”chmod -x your_script_name“。
在shell终端命令行执行shell脚本可显式地在命令行指定解释器,如”bash your_script_name“,此时,不必在脚本内声明解释器,而且脚本的可执行权限也不是必需的。如果没有在命令行显式地指定解释器,如“your_script_name”,而是把脚本当作一个命令来执行,此时,需要在脚本内声明解释器,脚本的可执行权限也是必需的。Bash运行shell脚本时,把特殊参数“0”设置为了脚本名称,而不是bash的名称,如果有其它的参数的话,就会设置对应的位置参数。