高性能Linux服务器运维实战:shell编程、监控告警、性能优化与实战案例
上QQ阅读APP看书,第一时间看更新

2.1 正则表达式与变量

2.1.1 正则表达式的组成与应用1.什么是正则表达式

正则表达式(Regular Expression,RE)就是由一系列特殊字符组成的字符串。其中每个特殊字符都被称为元字符,这些元字符并不表示它们字面上的含义,而是被解释为一些特定的含义。

正则表达式是由普通字符和元字符共同组成的集合,这个集合用来匹配(或指定)模式。正则表达式的主要功能是文本查询和字符串操作,正则表达式可以匹配文本的一个字符或字符集合。

例如,a、b、1、2等字符属于普通字符,普通字符可以按照字面意思理解,如a只能理解为英文的小写字母a,没有其他隐藏含义。而*、^、[ ]等元字符,shell赋予了它们超越字面意思的意义,如*符号的字面意义只是一个符号,而实际上却表示了重复前面的字符0次或多次的隐藏含义。

2.正则表达式的组成

一个正则表达式包含下列一项或多项。

➢ 一个字符集:这里所指的字符集只包含普通字符,这些字符只表示它们的字面含义。正则表达式的最简单形式就是只包含字符集,而不包含元字符。

➢ 锚:锚指定了正则表达式所要匹配的文本在文本行中所处的位置,如^和$就是锚。

➢ 修饰符:它们扩大或缩小(修改)了正则表达式匹配文本的范围。修饰符包含星号、括号和反斜杠。

正则表达式中常用的一些符号以及对应的意义见表2-1。

表2-1 正则表达式中符号含义

其中:

➢ *用于匹配前面一个普通字符的0次或多次重复,例如,hel*o,*符号前面的普通字符是l,*字符就表示匹配l字符0次或多次,如字符串helo、hello、hellllllo都可以由hel*o来表示。

➢ .用于匹配任意一个字符,例如,…73. 表示前面3个字符为任意字符,第4和第5个字符是7和3,最后一个字符为任意字符,如xcb738、4J973U都能匹配上述字符串。

➢ ^用于匹配行首,表示行首的字符是^字符后面的那个字符,例如:^cloud表示匹配以cloud开头的行

➢ $匹配行尾,$放在匹配字符之后,例如,micky$表示匹配以micky结尾的所有行,^$表示匹配空白行。

➢ []匹配字符集合,在正则表达式中,将匹配中括号字符集中的某一个字符,例如:

✧ [xyz]将会匹配字符x、y、或z。

✧ [c-n]匹配字符c~n之间的任意一个字符。

✧ [B-Pk-y]匹配从B~P,或者从k~y之间的任意一个字符。

✧ [a-z0-9]匹配任意小写字母或数字。

✧ [^b-d]将会匹配范围在b~d之外的任意一个字符。这就是使用^对字符集取反的一个实例。

✧ 将多个中括号字符集组合使用,能够匹配一般的单词或数字,例如,[Yy][Ee][Ss]能够匹配yes、Yes、YES、yEs等,[0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9]可以匹配社保码。

下面来看第1个例子,操作如下:

从输出可以看出,精确匹配了exp2.txt文件中所有的非字符内容。

➢ \用来转义某个特殊含义的字符,这意味着,这个特殊字符将会被解释为字面含义。例如:

➢ \$将会被解释成字符$,而不是RE中匹配行尾的特殊字符。相似的,\\将会被解释为字符\。

➢ 转义的尖括号\<...\>用于匹配单词边界,尖括号必须被转义才含有特殊的含义,否则它就表示尖括号的字面含义。

➢ \<the\>完整匹配单词the,不会匹配them、there、other等。

➢ \{\}系列符号表示前一个字符的重复次数。

✧ \{n\}匹配前面字符出现n次,如JO\{3\}B匹配JOOOB。

✧ \{n,\}匹配前面字符至少出现n次,如JO\{3,\}B匹配JOOOB、JOOOOB、JOOOOOB等字符串。

✧ \{n,m\}匹配前面字符出现n次与m次之间,如JO\{3,6\}B匹配JOOOB、JOOOOOOB等字符串。

例如,[a-z]\{5\}匹配5个小写英文字母,如hello、house等。

继续看第2个例子,操作如下:

可以看出,grep数字中精确匹配了iivey这个单词。

2.1.2 shell中的变量与应用

1.变量的定义与分类

变量用于保存有用信息,如路径名、文件名、数字等。Linux用户使用变量定制其工作环境,使系统获知用户相关的配置。变量本质上是存储数据的一个或多个计算机内存地址。

shell中的变量可分为如下几种。

➢ 用户自定义变量,如,myname,这类变量是由用户自己定义、修改和使用。

➢ shell环境变量PATH,这类变量是由系统维护,用于设置用户的shell工作环境,只有少数的变量用户可以修改其值。

➢ 位置参数变量(Positional Parameters),这类变量通过命令行给程序传递执行参数,可用shift命令实现位置参数的迁移。

➢ 内部参数变量(Special Parameters),这类变量是Bash预定义的特殊变量,用户不能修改其值。

2.变量的赋值

变量是某个值的名称,引用变量值称为变量替换,$符号是变量替换符号,如variable是变量名,那么$variable就表示变量的值。

变量赋值有两种格式:

切记等号的两边不可以有空格;如果值(value)中包含空格,则必须用双引号括起来,没有空格时也可以用引号,效果和不用一样;变量名只能包括大小写字母(a~z和A~Z)、数字(0~9)、下画线(_)等符号,并且变量名不能以数字开头,否则视为无效变量名,变量区分大小写。

3.变量声明和使用

要使用变量,首先要进行变量的声明,因为shell变量是弱类型的,因此不用声明变量的类型,变量声明与赋值的格式如下:

变量一旦声明和赋值完成,就可以进行引用了,变量引用的方法有两种:

两种引用方法,在不同环境可进行不同选择。一般规则是:如果变量名为一个字符时建议使用方式一,多于一个字符时建议使用方式二。例如,$a、${abc}。

要显示变量,可以通过echo命令。echo命令可以显示单个变量取值,变量名前加$即可,例如:

这里仍然建议,输出引用变量时加{}。如果变量名多于一个字符,不加大括号可能会引起不必要的错误。

4.变量清除与只读

当变量不再使用时,可以通过unset命令进行清除,unset命令清除变量的格式为:

有时想让某个变量变成只读,变量一旦设置为只读,任何用户不能对此变量进行重新赋值,此时可以使用readonly命令,设置变量只读格式如下:

下面来看个例子:

可以看出,变量只读后,无法清除和重新赋值。

5.内部参数变量

内部参数分两类,一类是命令行参数相关的,见表2-2。

表2-2 内部参数变量与含义

这里有两个变量需要注意:$*和$@都表示传递给脚本或函数的所有参数,但不被双引号(" ")包含时,都以$1、$2、…、$n的形式输出所有参数。但是当它们被双引号("")包含时,$*会将所有的参数作为一个整体,以$1$2 … $n的形式输出所有参数;而$@会将各个参数分开,以$1、$2、…、$n的形式输出所有参数。

另一类内部参数是与进程状态相关的,常见的参数见表2-3。

表2-3 与进程相关的内部参数与含义

下面看一个例子myscript1.sh,脚本内容如下:

这个例子集中演示了位置参数变量、内部参数变量的含义和输出。执行脚本,后面跟上对应的参数,即可看到对应的不同变量的输出。操作如下:

再来介绍一个IFS变量,shell脚本中有个变量叫IFS(Internal Field Seprator),内部域分隔符,IFS的默认值为空白(包括空格、Tab和新行),例如:

直接输出IFS是看不到的,把IFS转化为八进制就可以看到了,040是空格,011是Tab,012是换行符\n。最后一个012是因为echo默认是会换行的。

这里再来总结下$*和$@的区别,$*会根据IFS的不同来组合值,而$@则会将值用空格来组合值,推荐使用$@,而不是$*。

下面给出一个脚本myscript2.sh,通过输出就能看到它们的差别:

执行上面脚本,输出如下:

从这个脚本的执行结果,可以看出$@、$*以及与IFS的关系。

6.位置参数变量

位置参数是一种特殊的shell变量,用于从命令行向shell脚本传递参数。

$1表示第1个参数、$2表示第2个参数等,$0表示脚本的名字,从${10}开始,参数号需要用大括号括起来,如${10}、${11}、${100}等。那么位置参数主要用在什么地方呢?常用的环境有两个:退出/返回从shell命令/脚本的命令行接受参数或在调用shell函数时为其传递参数。

7.退出/返回状态

shell中有多种退出/返回状态,在写shell脚本的时候经常用到这些状态,那么如何获取脚本的退出/返回状态呢?可以通过$?来实现。$?用来退出/返回上一条语句或脚本执行的状态,常见状态如下:

其实可以在shell脚本中设置退出/返回状态,通过exit命令来实现。exit命令用于退出/返回脚本或当前shell,在退出/返回的时候,可以设置退出/返回状态码,方法如下:

其中,n是一个从0~255的整数,0表示成功退出/返回,非零表示遇到某种失败,返回值被保存在状态变量$?中。常见的退出/返回状态码见表2-4。

表2-4 常见的退出/返回状态码与含义

看下面几个例子:

通过命令的执行和输出,可以看出,每个退出/返回状态码的含义,明白了这些退出/返回状态码,就可以在写shell的时候进行调用,以判断命令是否执行成功。

8.命令替换

命令替换是指将命令的输出作为命令替换位置的文本,命令替换的作用是抽取一个命令的输出,然后使用=操作赋值到一个变量供以后使用。命令替换在shell编程中经常用到,有两种使用格式,分别是:

注意是反引号,也就是键盘〈Esc〉下面的那个键,看下面的例子:

两个例子都是将Nginx的进程数统计出来,然后赋给httpnum和httpnum1变量,在shell脚本中,变量这样定义后,下面就可以直接引用了。两种方式各有优缺点,推荐使用第2种方式。

9.read命令

read命令用来接收键盘输入内容为变量赋值,具体用法如下:

若省略变量名,则会将输入的内容存入默认REPLY变量中。看下面例子:

可以结合不同的引号为变量赋值,规则如下所述。

➢ 双引号"":允许通过$符号引用其他变量值。

➢ 单引号'':禁止引用其他变量值,$视为普通字符,因此引用变量时不要用单引号。

➢ 反撇号``:将命令执行的结果输出给变量。

看下面这个shell脚本myscript3.sh:

最后来执行脚本看看输出结果:

2.1.3 变量测试、截取与替换

1.变量测试的用法

shell支持变量测试和默认赋值,当一个变量不存在的时候,可以默认给此变量进行赋值。变量测试和赋值有多种方式,常见的有4种情况,见表2-5。

表2-5 变量测试的几种用法与含义

看下面这个操作过程,更能清晰地理解每个变量测试的含义:

2.字符串长度与截取

awk和sed可以进行文本中字符串的过滤、筛选和替换。其实,shell本身也支持这种功能,下面就来看看shell中字符串长度与截取的方法,见表2-6。

表2-6 字符串长度与截取的用法与含义

看下面这个例子,操作过程如下:

此外,shell还支持字符串替换,见表2-7。

表2-7 字符串替换用法

需要注意的是old中可以使用通配符。var可以是@或*,表示对每个位置参数进行替换,继续看下面的例子,操作过程如下:

3.变量的间接引用

先来看一个例子:

这个例子中,想让$str2输出Hello World,那么echo $str2将输出什么呢?是否能够满足要求?显然不能,上面这个输出中,最后echo $str2会输出的值为str1,那么如何才能输出所需要的值呢?可以这样执行:

上面两个命令都能实现将str1的值间接的赋给str2,最后结果为Hello World,满足间接赋值要求。再来看个例子:

那么看下面这几个组合输出什么内容:

来执行看看结果:

很显然,第2个是满足需要的,通过间接引用变量,实现了变量值的替换。

4.同时输出多行信息

同时输出多行信息也经常会使用,有两种方法可以实现,第1种是使用echo命令,用法如下:

注意,多行内容中不能出现双引号,否则echo提前结束,若确实需要使用双引号,需使用转义字符\。同时输出多行信息的第2个方法是使用here file,方法如下:

注意:END可以是任意字符串,只要上下一致即可,多行内容中不能出现内容为END开始的行,否则cat提前结束。