shell脚本编程之基础篇(二)

==============================================================================

概述:


==============================================================================

退出状态

进程使用退出状态来报告成功或失败

0 代表成功,1-255代表失败

$? 变量保存最近的命令退出状态 (查看:echo $?)存的是最后一条命令的结果,中间即使有错误,也不管。

脚本的状态返回值

默认是脚本中执行的最后一条命令的状态返回值;

自定义状态退出状态码;

  • exit[n]:自定义退出状态码;(exit退出的是当前shell)

注意:

  • 脚本中一旦遇到exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字

  • 如果未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码

演示:  

 1.进程使用退出状态

-c1代表一个数据包,-W代表一秒钟
[root@centos7 ~]# ping -c1 -W1 192.168.1.116 &> /devnull
[root@centos7 ~]# echo $?
0   # 成功,此主机正在使用


[root@centos7 ~]# ping -c1 -W1 192.168.1.112 &> /devnull
[root@centos7 ~]# echo $?
1   # 失败,此主机没有使用

条件测试

作用:

  • 判断某需求是否满足,需要由测试机制来实现;

  • 专用的测试表达式需要由测试命令辅助完成测试过程;

如何编写测试表达式以实现所需的测试:

执行命令,并利用状态返回值来判断:

  • 0:成功

  • 1-255:失败

测试表达式:

  • test EXPRESSION;

  • [ EXPRESSION ];

  • ` EXPRESSION `;特定情况下使用。最稳妥(支持正则表达式)

注意:

  • EXPRESSION前后必须有空白字符,否则语法错误。

条件性的执行逻辑操作符

根据退出状态而定,命令可以有条件地运行

  • && :代表条件性的AND THEN;

  • || :代表条件性的OR ELSE

演示:

[root@centos7 ~]# ping -c1 -W1 192.168.1.1 &> /dev/null && echo "The host is up"
The host is up

# 表示 如果能 ping 通这个主机 就输出 up,不能就输出 down;一定是先与后或
[root@centos7 ~]# ping -c1 -W1 192.168.1.1 &> /dev/null && echo "The host is up" || echo "The host is down"
The host is up

[root@centos7 ~]# ping -c1 -W1 192.168.1.114 &> /dev/null && echo "The host is up" || echo "The host is down"
The host is down

# 表示 如果能 ping 通这个主机 就输出 up,不能就输出 down,且返回值为 220。这里的括号表示在子shell中执行
[root@centos7 ~]# ping -c1 -W1 192.168.1.114 &> /dev/null && echo "The host is up" || (echo "The host is down" ;exit 220)
The host is down
[root@centos7 ~]# echo $?
220

bash的测试类型(数值,字符串,文件测试)

 1.数值测试

数值测试

作用:数值比较

操作符

  • -eq:是否等于;[ $num1 -eq $num2 ];

  • -ne:是否不等于;

  • -gt:是否大于;

  • -ge:是否大于等于;

  • -lt:是否小于;

  • -le:是否小于等于;

演示:

[root@centos7 ~]# test 2 -eq 3
[root@centos7 ~]# echo $?
1
[root@centos7 ~]# test 2 -ne 3
[root@centos7 ~]# echo $?
0
[root@centos7 ~]# test 2 -gt 3
[root@centos7 ~]# echo $?
1
[root@centos7 ~]# test 2 -lt 3
[root@centos7 ~]# echo $?
0

 2.字符串测试

字符串测试

  • 字符串比较要加引号;

  • 尽量要使用` `

操作符 

  • ==:是否等于;

  • >:ascii码是否大于ascii码;

  • <:ascii码是否小于ascii码

  • !=:是否不等于;

  • =~:左侧字符串是否能够被右侧的PATTERN所匹配(扩展表达式)

  • -z "STRING":判断指定的字符串是否为空,空为真,不空为假,字符串一定要加双引号;

  • -n "STRING":判断指定的字符串是否不空,不空为真,空为假;

注意:

  • 字符串比较要加引号,表示引用,做变量替换要加双引号,不做变量替换用单引号

  • 要尽量使用 ` `

  • 在 [ ] 中做变量替换一定要加双引号,但是如果使用 ` ` ,可以不加引号

演示:

[root@centos7 ~]# [ tom == Tom ]
[root@centos7 ~]# echo $?
1
[root@centos7 ~]# [ tom == tom ]
[root@centos7 ~]# echo $?
0

# 变量替换要尽量用双引号,如果不用的话,如果变量不存在为空,会报错
[root@centos7 ~]# [ tom == $name ]
-bash: [: tom: 期待一元表达式
[root@centos7 ~]# [ tom == "$name" ]  
[root@centos7 ~]# echo $?
1
[root@centos7 bin]# [[ tom == $name ]]
[root@centos7 bin]# echo $?
1

[root@centos7 ~]# name=xiu
[root@centos7 ~]# [ tom == $name ]
[root@centos7 ~]# echo $?
1
[root@centos7 ~]# [ tom == "$name" ]
[root@centos7 ~]# echo $?
1

# 要尽量使用 [[ ]]
[root@centos7 ~]# [ 'a' > 'b' ]
[root@centos7 ~]# echo $?
0
[root@centos7 ~]# [ 'a' < 'b' ]
[root@centos7 ~]# echo $?
0
[root@centos7 ~]# [[ 'a' > 'b' ]]
[root@centos7 ~]# echo $?
1
[root@centos7 ~]# [[ 'a' < 'b' ]]
[root@centos7 ~]# echo $?
0

# 在 [] 中变量替换一定要加双引号,但是在 [[ ]] 中可以不加
[root@centos7 bin]# [ -z $a ] && echo yes || echo no
yes
[root@centos7 bin]# [ -n $a ] && echo yes || echo no
yes
[root@centos7 bin]# [ -n "$a" ] && echo yes || echo no
no
[root@centos7 bin]# [ -z "$a" ] && echo yes || echo no
yes
[root@centos7 bin]# [[ -n $a ]] && echo yes || echo no
no
[root@centos7 bin]# [[ -z $a ]] && echo yes || echo no
yes
#============================================================================

[root@centos7 ~]# name=haha
[root@centos7 ~]# echo $name
haha
[root@centos7 ~]# [[ "$name" =~ ha ]]
[root@centos7 ~]# echo $?
0
[root@centos7 ~]# [[ "$name" =~ h ]]
[root@centos7 ~]# echo $?
0
[root@centos7 ~]# [[ "$name" =~ hx ]]
[root@centos7 ~]# echo $?
1
[root@centos7 ~]# [[ "$name" =~ xx ]]
[root@centos7 ~]# echo $?
1

 3.文件测试

存在性测试

  • -a FILE:同-e

  • -e FILE:文件存在性测试,存在为真,否则为假;

存在性及类型测试

  • -b FILE:是否存在且为块设备文件;

  • -c FILE:是否存在且为字符设备文件;

  • -d FILE:是否存在且为目录文件;

  • -f FILE:是否存在且为普通文件;

  • -h FILE 或-L FILE:存在且为符号链接文件;

  • -p FILE:是否存在且为命名管道文件;

  • -S FILE:是否存在且为套接字文件;

文件权限测试

  • -r FILE:是否存在且对当前用户可读;

  • -w FILE:是否存在且对当前用户可写;

  • -x FILE:是否存在且当前用户可执行

文件特殊权限测试

  • -g FILE:是否存在且拥有sgid权限;

  • -u FILE:是否存在且拥有suid权限;

  • -k FILE:是否存在且拥有sticky权限;

文件是否有内容

  • -s FILE:是否存在且非空(即,文件是否有内容);

文件时间戳测试

  • -N FILE:文件自从上一次读操作后是否被修改过;

从属关系测试

  • -O FILE:当前用户是否为文件的属主;

  • -G FILE:当前用户是否属于文件的属组;

文件是否打开

  • -t fd:fd表示文件描述符是否已经打开且与某终端相关;

双目测试

  • FILE1 -ef FILE2:FILE1与FILE2是否指向同一个设备上的相同inode;

  • FILE1 -nt FILE2:FILE1是否新于FILE2;(修改时间)

  • FILE1 -ot FILE2:FILE1是否旧于FILE2;

注意:

  • 文件测试一般使用 [ ] 就可以

演示:

 1.文件存在性测试

[root@centos7 ~]# [ -e /etc/fstab ]
[root@centos7 ~]# echo $?
0
[root@centos7 ~]# [ -e /etc/rc.d/rc.sysinit ]
[root@centos7 ~]# echo $?
1

 2.文件存在性及类型测试     

[root@centos7 ~]# [ -b /dev/sda ]
[root@centos7 ~]# echo $?
0
[root@centos7 ~]# [ -b /dev/sdb ]
[root@centos7 ~]# echo $?
1
[root@centos7 ~]# [ -d /etc ]
[root@centos7 ~]# echo $?
0

[root@centos7 ~]# [ -L /etc/redhat-release ]
[root@centos7 ~]# echo $?
0
[root@centos7 ~]# ll /etc/redhat-release 
lrwxrwxrwx. 1 root root 14 11月  6 18:30 /etc/redhat-release -> centos-release

 3.文件权限及特殊权限测试      

[root@centos7 ~]# ll /etc/shadow
---------- 1 root root 1400 2月  20 14:12 /etc/shadow

[centos@centos7 ~]$ whoami
centos
[centos@centos7 ~]$ [ -r /etc/shadow ]
[centos@centos7 ~]$ echo $?
1
[centos@centos7 ~]$ [ -r /etc/passwd ]
[centos@centos7 ~]$ echo $?
0
[centos@centos7 ~]$ [ -w /etc/passwd ]
[centos@centos7 ~]$ echo $?
1

# 对于root用户来讲,rw 权限是以实际为主,但执行权限是起作用的
[root@centos7 ~]# whoami
root
[root@centos7 ~]# [ -r /etc/shadow ]
[root@centos7 ~]# echo $?
0
[root@centos7 ~]# [ -w /etc/shadow ]
[root@centos7 ~]# echo $?
0
[root@centos7 ~]# [ -x /etc/shadow ]
[root@centos7 ~]# echo $?
1

#==========================================================================

[root@centos7 ~]# [ -u /usr/bin/passwd ]
[root@centos7 ~]# echo $?
0 # 说明 /usr/bin/passwd  拥有suid权限

[root@centos7 ~]# ll /usr/bin/passwd 
-rwsr-xr-x. 1 root root 27832 6月  10 2014 /usr/bin/passwd

4.文件是否有内容测试

[root@centos7 ~]# touch /tmp/hello
[root@centos7 ~]# [ -s /tmp/hello ]
[root@centos7 ~]# echo $?
1

[root@centos7 ~]# [ -s /etc/fstab ]
[root@centos7 ~]# echo $?
0

5.双目测试

[root@centos7 ~]# touch f1
[root@centos7 ~]# ln f1 f22 # 创建f22 的硬链接为f1

[root@centos7 ~]# [ f1 -ef f22 ]  # 是否指向同一设备上相同的inode,也就是说是否为硬链接
[root@centos7 ~]# echo $?
0

[root@centos7 ~]# ll -i f1 f22
201391291 -rw-r--r-- 2 root root 1024 2月  23 18:39 f1
201391291 -rw-r--r-- 2 root root 1024 2月  23 18:39 f22

 4.组合测试条件

第一种方式:逻辑判断符

  • OMMAND1 && COMMAND2 :并且;

  • COMMAND1 || COMMAND2   :或者;

  • !COMMAND :

示例:

  • [ -O FILE ] && [ -r FILE ]

第二种方式:布尔运算符

  • EXPRESSION1 -a EXPRESSION2 :并且

  • EXPRESSION1 -o EXPRESSION2 :或者

  • ! EXPRESSION:取反

注意:

  •  布尔运算符(-a ,-o, !)只能用在 [ ] 表达式中;如:[ 1 -eq 1 -a 2 -eq 2 ]为true;[ 1 -eq 1 -o 2 -eq 1 ]为true

  • 逻辑运算符(&&, ||)只能用在 ` `和(( ))中使用

总结:

1.条件表达式

表达式

示例

[ expression ] [ 1 -eq 1 ] 
` expression ` ` 1 -eq 1 `
test expression test 1 -eq 1 ,等同于[]

2.整数比较符

比较符

描述

示例

-eq,equal 等于 [ 1 -eq 1 ]为true
-ne,not equal 不等于 [ 1 -ne 1 ]为false
-gt,greater than 大于 [ 2 -gt 1 ]为true
-lt,lesser than 小于 [ 2 -lt 1 ]为false
-ge,greater or equal 大于或等于 [ 2 -gt 1 ]为true
-le,lesser or equal 小于或等于 [ 2 -le 1 ]为false

3.字符串比较符

运算符

描述

示例

== 等于 [ "a" == "a"  ]为true
!= 不等于 [ "a" != "a"  ]为false
> 大于,判断字符串时根据ASCII码表顺序,不常用

在[]表达式中:[ 2 \> 1 ]为true

在[[]]表达式中:[[ 2 > 1 ]]为true

在(())表达式中:(( 3 > 2 ))为true

< 小于,判断字符串时根据ASCII码表顺序,不常用

在[]表达式中:[ 2 \< 1 ]为false

在[[]]表达式中:[[ 2 < 1 ]]为false

在(())表达式中:(( 3 < 2 ))为false

>= 大于等于 在(())表达式中:(( 3 >= 2 ))为true
<= 小于等于 在(())表达式中:(( 3 <= 2 ))为false
-n 字符串长度不等于0为真

VAR1=1;VAR2=""

[ -n "$VAR1" ]为true

[ -n "$VAR2" ]为false

-z 字符串长度等于0为真

VAR1=1;VAR2=""

[ -z "$VAR1" ]为false

[ -z "$VAR2" ]为true

str 字符串存在为真

VAR1=1;VAR2=""

[ $VAR1 ]为true

[ $VAR2 ]为false

 

4.文件测试

测试符

描述

示例

-e 文件或目录存在为真 [ -e path ] path存在为true
-f 文件存在为真 [ -f file_path ] 文件存在为true
-d 目录存在为真 [ -d dir_path ] 目录存在为true
-r 有读权限为真 [ -r file_path ] file_path有读权限为true
-w 有写权限为真 [ -w file_path ] file_path有写权限为true
-x 有执行权限为真 [ -x file_path ] file_path有执行权限为true
-s 文件存在并且大小大于0为真 [ -s file_path ] file_path存在并且大小大于0为true

5.布尔运算符

运算符

描述

示例

! 非关系,条件结果取反 [ ! 1 -eq 2 ]为true
-a 和关系,在[]表达式中使用 [ 1 -eq 1 -a 2 -eq 2 ]为true
-o 或关系,在[]表达式中使用 [ 1 -eq 1 -o 2 -eq 1 ]为true

6.逻辑判断符

判断符

描述

示例

&& 逻辑与,在` `和(( ))表达式中或判断表达式是否为真时使用

[[ 1 -eq 1 && 2 -eq 2 ]]为true

(( 1 == 1 && 2 == 2 ))为true

[ 1 -eq 1 ] && echo yes 表示:如果&&前面表达式为true则执行后面的

|| 逻辑或,在` `和(( ))表达式中或判断表达式是否为真时使用

[[ 1 -eq 1 || 2 -eq 1 ]]为true

(( 1 == 1 || 2 == 2 ))为true

[ 1 -eq 2 ] || echo yes 表示:如果||前面表达式为false则执行后面的

7.整数运算

运算符

描述

+ 加法
- 减法
* 乘法
/ 除法
% 取余


运算表达式

示例

$(()) $((1+1))
$[] $[1+1]


除了Shell本身的算数运算表达式,还有几个命令支持复杂的算数运算:

命令

描述

示例

let 赋值并运算,支持++、--

let VAR=(1+2)*3 ; echo $VAR

x=10 ; y=5

let x++;echo $x 每执行一次x加1

let y--;echo $y 每执行一次y减1

let x+=2 每执行一次x加2

let x-=2 每执行一次x减2

expr 乘法*需要加反斜杠转义\*

expr 1 \* 2  运算符两边必须有空格

expr \( 1 + 2 \) \* 2  使用双括号时要转义

bc 计算器,支持浮点运算、平方等

bc本身就是一个计算器,可直接输入命令,进入解释器。

echo 1 + 2 |bc 将管道符前面标准输出作为bc的标准输入

echo "1.2+2" |bc

echo "10^10" |bc 

echo 'scale=2;10/3' |bc  用scale保留两位小数点





看到这里,想一想里面所讲的小括号、中括号的用途,是不是有点懵逼了。那我们总结一下!

( )

用途1:在运算中,先计算小括号里面的内容

用途2:数组

用途3:匹配分组

(( ))

用途1:表达式,不支持-eq这类的运算符。不支持-a和-o,支持<=、>=、<、>这类比较符和&&、||

用途2:C语言风格的for(())表达式

$( ) 执行Shell命令,与反撇号等效
$(( ))

用途1:简单算数运算

用途2:支持三目运算符 $(( 表达式?数字:数字 ))

[ ] 条件表达式,里面不支持逻辑判断符(&&,|| )
` ` 条件表达式,里面不支持-a和-o,不支持<=和>=比较符,支持-eq、<、>这类比较符。支持=~模式匹配,也可以不用双引号也不会影响原意,比[]更加通用
$[ ] 简单算数运算
{ } 对逗号(,)和点点(...)起作用,比如touch {1,2}创建1和2文件,touch {1..3}创建1、2和3文件
${ }

用途1:引用变量

用途2:字符串处理

          



练习:

  1、编写脚本/root/bin/systeminfo.sh,显示当前主机系统信息,包括主机名,IPv4地址,操作系统版本,内核版本,CPU型号,内存大小,硬盘大小。


[root@centos7 bin]# cat systeminfo.sh 
#!/bin/bash

# 取 IP 地址,采用sed方法,掐头去尾
server_ip=$(ifconfig |sed -n '2p' |sed -e 's@^.*inet@@' -e 's@net.*@@')
CPUmod=$(lscpu | grep -i "model name:")
Meninfo=$(free -h | sed -n '2p' | tr -s ' ' | cut -d' ' -f2)
DISKinfo=$(fdisk -l |grep "Disk /dev/[sh]d[a-z]" |sed -r 's@.* ([0-9]+.*GB).*@\1@')

echo 'hostname :'$(hostname)
echo 'hostIP:'$server_ip
echo 'OS version:'$(cat /etc/redhat-release)
echo 'Kernel version:'$(uname -r)
echo 'CPU '$CPUmod
echo 'Memory :'$Meninfo
echo 'Harddisk:'$DISKinfo

# 执行脚本
[root@centos7 bin]# chmod +x systeminfo.sh
[root@centos7 bin]# ll systeminfo.sh
-rwxr-xr-x 1 root root 522 Feb 23 21:42 systeminfo.sh
[root@centos7 bin]# cd
[root@centos7 ~]# systeminfo.sh
hostname :centos7
hostIP: 192.168.1.112
OS version:CentOS Linux release 7.2.1511 (Core)
Kernel version:3.10.0-327.el7.x86_64
CPU Model name: Intel(R) Core(TM) i3-2328M CPU @ 2.20GHz
Memory :977M
Harddisk:85.9 GB