随堂练习 shell脚本(二)
算术运算
Shell允许在某些情况下对算术表达式进行求值,比如:let和declare 内置命令,(( ))复合命令和算术扩展。求值以固定宽度的整数进行,不检查溢出,尽管除以0 被困并标记为错误。运算符及其优先级,关联性和值与C语言相同。以下运算符列表分组为等优先级运算符级别。级别按降序排列优先。
注意:bash 只支持整数,不支持小数
id++ id-- variable post-increment and post-decrement
++id --id variable pre-increment and pre-decrement
- + unary minus and plus
! ~ logical and bitwise negation
** exponentiation 乘方
* / % multiplication, division, remainder, %表示取模,即取余数,示例:9%4=1,5%3=2
+ - addition, subtraction
<< >> l eft and right bitwise shifts
<= >= < > comparison
== != equality and inequality
& bitwise AND
^ bitwise exclusive OR
| bitwise OR
&& l ogical AND
|| logical OR
expr?expr:expr conditional operator
= *= /= %= += -= <<= >>= &= ^= |= assignment
expr1 , expr2 comma
乘法符号有些场景中需要转义
实现算术运算:
1)let var =算术表达式
2)((var=算术表达式)) 和上面等价
3)var=$[算术表达式]
4)var=$((算术表达式))
5)var=$(expr arg1 arg2 ... )
6)declare -i var = 数值
7)echo '算术表达式' | bc
内建的随机数生成器变量:
$RANDOM 取值范围:0 - 32767
范例
#生成 0 - 49 之间随机数
echo $[$RANDOM%50]
# 随机字体颜色
[root@centos8 ~]# echo -e "\033[1;$[RANDOM%7+31]hello\033[0m"
hello
增强型赋值:
+= i+=10 相当于 i=i+10
-= i-=j 相当于 i=i-j
*=
/=
%=
++ i++,++i 相当于 i=i+1
-- i--,--i 相当于 i=i-1
格式
let varOPERvalue
[root@centos8 ~]# let i=10*2
[root@centos8 ~]# echo $i
20
[root@centos8 ~]# ((j=i+10))
[root@centos8 ~]# echo $j
30
范例
#自加3后自赋值
let count+=3
[root@centos8 ~]# i=10
[root@centos8 ~]# let i+=20 #相当于let i=i+20
[root@centos8 ~]# echo $i
30
[root@centos8 ~]# j=20
[root@centos8 ~]# let i*=j
[root@centos8 ~]# echo $i
600
范例
#自增,自减
let var+=1
let var++
let var-=1
let var--
[root@centos8 ~]# unset i j ; i=1; let j=i++; echo "i=$i,j=$j"
i=2,j=1
[root@centos8 ~]# unset i j ; i=1; let j=++i; echo "i=$i,j=$j"
i=2,j=2
范例
[root@centos8 ~]# expr 2 * 3
expr: syntax error: unexpected argument ‘anaconda-ks.cfg’
[root@centos8 ~]# ls
anaconda-ks.cfg
[root@centos8 ~]# expr 2 \* 3
6
范例
[root@centos8 ~]# echo "scale=3;20/3"|bc
6.666
范例
[root@centos8 ~]# i=10
[root@centos8 ~]# j=20
[root@centos8 ~]# declare -i result=i*j
[root@centos8 ~]# echo $result
200
范例:今有雉兔同笼,上有三十五头,下有九十四足,问雉兔各几何?
#!/bin/bash
HEAD=$1
FOOT=$2
((RABBIT=(FOOT-HEAD-HEAD)/2))
((CHOOK=HEAD-RABBIT))
echo RABBIT: $RABBIT
echo CHOOK: $CHOOK
逻辑运算
1 true
0 false
与:&:和0相与,结果为0,和1相与,结果保留原值
1 与 1 = 1
1 与 0 = 0
0 与 1 = 0
0 与 0 = 0
或:|:和1相或结果为1,和0相或,结果保留原值
1 或 1 = 1
1 或 0 = 1
0 或 1 = 1
0 或 0 = 0
非:!
!1 = 0 ! true
! 0 = 1 ! false
异或:^
异或的两个值,相同为假,不同为真。两个数字X,Y异或得到结果Z,Z再和任意两者之一X异或,将得出另一个值Y
1 ^ 1 = 0
0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
范例
[root@centos8 ~]# true
[root@centos8 ~]# echo $?
0
[root@centos8 ~]# false
[root@centos8 ~]# echo $?
1
[root@centos8 ~]# ! true
[root@centos8 ~]# echo $?
1
[root@centos8 ~]# ! false
[root@centos8 ~]# echo $?
0
[root@centos8 ~]# x=10;y=20;temp=$x;x=$y;y=$temp;echo x=$x,y=$y
x=20,y=10
[root@centos8 ~]# x=10;y=20;x=$[x^y];y=$[x^y];x=$[x^y];echo x=$x,y=$y
x=20,y=10
短路运算
短路与
CMD1 短路与 CMD2
第一个CMD1结果为真 (1),第二个CMD2必须要参与运算,才能得到最终的结果
第一个CMD1结果为假 (0),总的结果必定为0,因此不需要执行CMD2
短路或
CMD1 短路或 CMD2
第一个CMD1结果为真 (1),总的结果必定为1,因此不需要执行CMD2
第一个CMD1结果为假 (0),第二个CMD2 必须要参与运算,才能得到最终的结果
条件测试命令
条件测试:判断某需求是否满足,需要由测试机制来实现,专用的测试表达式需要由测试命令辅助完成
测试过程
,实现评估布尔声明,以便用在条件性环境下进行执行
若真,则状态码变量 $? 返回0
若假,则状态码变量 $? 返回1
条件测试命令
test EXPRESSION
[ EXPRESSION ] #和test 等价,建议使用 [ ]
[[ EXPRESSION ]]
注意:EXPRESSION前后必须有空白字符
变量测试
# 判断 NAME 变量是否定义
[ -v NAME ]
# 判断 NAME 变量是否定义并且是名称引用,bash 4.4新特性
[ -R NAME ]
范例
[root@centos8 ~]# unset x
[root@centos8 ~]# test -v x
[root@centos8 ~]# echo $?
1
[root@centos8 ~]# x=10
[root@centos8 ~]# test -v x
[root@centos8 ~]# echo $?
0
[root@centos8 ~]# y=
[root@centos8 ~]# test -v y
[root@centos8 ~]# echo $?
0
#注意 [ ] 需要空格,否则会报下面错误
[root@centos8 ~]# [-v y]
-bash: [-v: command not found
[root@centos8 ~]# [ -v y ]
[root@centos8 ~]# echo $?
0
数值测试
-eq 是否等于
-ne 是否不等于
-gt 是否大于
-ge 是否大于等于
-lt 是否小于
-le 是否小于等于
范例
[root@centos8 ~]# i=10
[root@centos8 ~]# j=8
[root@centos8 ~]# [ $i -lt $j ]
[root@centos8 ~]# echo $?
1
[root@centos8 ~]# [ $i -gt $j ]
[root@centos8 ~]# echo $?
0
[root@centos8 ~]# [ i -gt j ]
-bash: [: i: integer expression expected
算术表达式比较
<=
>=
<
>
范例
[root@centos8 ~]# x=10;y=20;(( x > y )); echo $?
1
[root@centos8 ~]# x=10;y=20;(( x < y )); echo $?
0
字符串测试
test 和 [ ] 用法
-z STRING 字符串是否为空,没定义或空为真,不空为假,
-n STRING 字符串是否不空,不空为真,空为假
STRING 同上
STRING1 = STRING2 是否等于,注意 = 前后有空格
STRING1 != STRING2 是否不等于
> ascii 码是否大于 ascii 码
< 是否小于
[[]] 用法
[[ expression ]] 用法
== 左侧字符串是否和右侧的PATTERN相同
注意:此表达式用于[[ ]]中,PATTERN为通配符
=~ 左侧字符串是否能够被右侧的正则表达式的PATTERN所匹配
注意: 此表达式用于[[ ]]中;扩展的正则表达式
建议:当使用正则表达式或通配符使用[[ ]],其它情况一般使用 [ ]
范例 :使用 [ ]
[root@centos8 ~]# unset str
[root@centos8 ~]# [ -z "$str" ]
[root@centos8 ~]# echo $?
0
[root@centos8 ~]# str=""
[root@centos8 ~]# [ -z "$str" ]
[root@centos8 ~]# echo $?
0
[root@centos8 ~]# str=" "
[root@centos8 ~]# [ -z "$str" ]
[root@centos8 ~]# echo $?
1
[root@centos8 ~]# [ -n "$str" ]
[root@centos8 ~]# echo $?
0
[root@centos8 ~]# unset str
[root@centos8 ~]# [ -n "$str" ]
[root@centos8 ~]# echo $?
1
[root@centos8 ~]# [ "$str" ]
[root@centos8 ~]# echo $?
1
[root@centos8 ~]# str=magedu
[root@centos8 ~]# [ "$str" ]
[root@centos8 ~]# echo $?
0
[root@centos8 ~]# str=magedu
[root@centos8 ~]# [ "$str" ]
[root@centos8 ~]# echo $?
0
[root@centos8 ~]# str1=magedu
[root@centos8 ~]# str2=mage
[root@centos8 ~]# [ $str1 = $str2 ]
[root@centos8 ~]# echo $?
1
[root@centos8 ~]# str2=magedu
[root@centos8 ~]# [ $str1 = $str2 ]
[root@centos8 ~]# echo $?
0
范例:在比较字符串时,建议变量放在“ ”中
[root@centos8 ~]# [ "$NAME" ]
[root@centos8 ~]# NAME="I love linux"
[root@centos8 ~]# [ $NAME ]
-bash: [: love: binary operator expected
[root@centos8 ~]# [ "$NAME" ]
[root@centos8 ~]# echo $?
0
[root@centos8 ~]# [ I love linux ]
-bash: [: love: binary operator expected
范例:使用 [[ ]]
#通配符
[root@centos8 ~]# FILE=test.log
[root@centos8 ~]# [[ "$FILE" == *.log ]]
[root@centos8 ~]# echo $?
0
[root@centos8 ~]# FILE=test.txt
[root@centos8 ~]# [[ "$FILE" == *.log ]]
[root@centos8 ~]# echo $?
1
[root@centos8 ~]# [[ "$FILE" != *.log ]]
[root@centos8 ~]# echo $?
0
#正则表达式
[root@centos8 ~]# [[ "$FILE" =~ \.log$ ]]
[root@centos8 ~]# echo $?
1
[root@centos8 ~]# FILE=test.log
[root@centos8 ~]# [[ "$FILE" =~ \.log$ ]]
[root@centos8 ~]# echo $?
0
[root@centos8 ~]# N=100
[root@centos8 ~]# [[ "$N" =~ ^[0-9]+$ ]]
[root@centos8 ~]# echo $?
0
[root@centos8 ~]# N=Magedu10
[root@centos8 ~]# [[ "$N" =~ ^[0-9]+$ ]]
[root@centos8 ~]# echo $?
1
[root@centos8 ~]# IP=1.2.3.4
[root@centos8 ~]# [[ "$IP" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]
[root@centos8 ~]# echo $?
0
[root@centos8 ~]# IP=1.2.3.4567
[root@centos8 ~]# [[ "$IP" =~ ^([0-9]{1,3}.){3}[0-9]{1,3}$ ]]
[root@centos8 ~]# echo $?
1
[root@centos8 ~]# [[ $IP =~ ^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3} ([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ ]]
[root@centos8 ~]# echo $?
1
#通配符
[root@centos8 ~]# NAME="linux1"
[root@centos8 ~]# [[ "$NAME" == linux* ]]
[root@centos8 ~]# echo $?
0
[root@centos8 ~]# [[ "$NAME" == "linux*" ]]
[root@centos8 ~]# echo $?
1
[root@centos8 ~]# NAME="linux*"
[root@centos8 ~]# [[ "$NAME" == "linux*" ]]
[root@centos8 ~]# echo $?
0
# 结论:[[ == ]] == 右侧的 * 做为通配符,不要加“”,只想做为*, 需要加“” 或转义
文件测试
存在性测试
-a FILE 同 -e
-e FILE 文件存在性测试,存在为真,否则为假
-b FILE 是否存在且为块设备文件
-c FILE 是否存在且为字符设备文件
-d FILE 是否存在且为目录文件
-f FILE 是否存在且为普通文件
-h FILE 或 -L FILE 存在且为符号链接文件
-p FILE 是否存在且为命名管道文件
-s FILE 是否存在且为套接字文件
范例
[root@centos8 ~]# [ -a /etc/nologin ]
[root@centos8 ~]# echo $?
1
[root@centos8 ~]# ! [ -a /etc/nologin ]
[root@centos8 ~]# echo $?
0
[root@centos8 ~]# [ -d /etc ]
[root@centos8 ~]# echo $?
0
[root@centos8 ~]# [ -d /etc/issue ]
[root@centos8 ~]# echo $?
1
[root@centos8 ~]# [ -L /bin ]
[root@centos8 ~]# echo $?
0
[root@centos8 ~]# [ -L /bin/ ]
[root@centos8 ~]# echo $?
1
文件权限测试:
-r FILE 是否存在且可读
-w FILE 是否存在且可写
-x FILE 是否存在且可执行
-u FILE 是否存在且拥有 suid 权限
-g FILE 是否存在且拥有 sgid 权限
-k FILE 是否存在且拥有 Sticky 权限
注意:最终结果由用户对文件的实际权限决定,而非文件属性决定
范例
[root@centos8 ~]# [ -w /etc/shadow ]
[root@centos8 ~]# echo $?
0
[root@centos8 ~]# [ -x /etc/shadow ]
[root@centos8 ~]# echo $?
1
[root@centos8 ~]# [ -w test.txt ]
[root@centos8 ~]# echo $?
0
[root@centos8 ~]# chattr +i test.txt
[root@centos8 ~]# lsattr test.txt
----i-------------- nianling.txt
[root@centos8 ~]# [ -w test.txt ]
[root@centos8 ~]# echo $?
1
[root@centos8 ~]# chattr -i test.txt
[root@centos8 ~]# [ -w test.txt ]
[root@centos8 ~]# echo $?
0
文件属性测试
-s FILE #是否存在且非空
-t fd #fd 文件描述符是否在某终端已经打开
-N FILE #文件自从上一次被读取之后是否被修改过
-O FILE #当前有效用户是否为文件属主
-G FILE #当前有效用户是否为文件属组
FILE1 -ef FILE2 #FILE1是否是FILE2的硬链接
FILE1 -nt FILE2 #FILE1是否新于FILE2(mtime)
FILE1 -ot FILE2 #FILE1是否旧于FILE2
关于 ()和 {}
(CMD1;CMD2;...)和 { CMD1;CMD2;...; } 都可以将多个命令组合在一起,批量执行
[root@centos8 ~]# man bash
( list ) 会开启子shell,并且list中变量赋值及内部命令执行后,将不再影响后续的环境,帮助参看:man bash 搜索(list)
{ list; } 不会启子shell, 在当前shell中运行,会影响当前shell环境帮助参看:man bash 搜索{ list; }
范例: () 和 {}
[root@centos8 ~]# name=mage;(echo $name;name=wang;echo $name );echo $name
mage
wang
mage
[root@centos8 ~]# name=mage;{ echo $name;name=wang;echo $name; } ;echo $name
mage
wang
wang
[root@centos8 ~]# umask
0022
[root@centos8 ~]# (umask 066;touch f1.txt)
[root@centos8 ~]# ll f1.txt
-rw------- 1 root root 0 Dec 23 16:58 f1.txt
[root@centos8 ~]# umask
0022
[root@centos8 ~]# ( cd /data;ls )
test.log
[root@centos8 ~]# pwd
/root
[root@centos8 ~]# { cd /data;ls; }
test.log
[root@centos8 data]# pwd
/data
#()会开启子shell
[root@centos8 ~]# echo $BASHPID
1920
[root@centos8 ~]# ( echo $BASHPID;sleep 100)
1979
[root@centos8 ~]# pstree -p
├─sshd(719)───sshd(1906)───sshd(1919)─┬─bash(1920)───bash(1979)───sleep(1980)
#{ } 不会开启子shell
[root@centos8 ~]# echo $BASHPID
1920
[root@centos8 ~]# { echo $BASHPID; }
1920
组合测试条件
第一种方式 [ ]
[ EXPRESSION1 -a EXPRESSION2 ] 并且,EXPRESSION1和EXPRESSION2都是真,结果才为真
[ EXPRESSION1 -o EXPRESSION2 ] 或者,EXPRESSION1和EXPRESSION2只要有一个真,结果就为真
[ ! EXPRESSION ] 取反
说明: -a 和 -o 需要使用测试命令进行,[[ ]] 不支持
范例
[root@centos8 ~]# ll /data/scrips/test.sh
-rw-r--r-- 1 root root 382 Dec 23 09:32 /data/scripts/test.sh
[root@centos8 ~]# [ -f $FILE -a -x $FILE ]
[root@centos8 ~]# echo $?
1
[root@centos8 ~]# chmod +x /data/scripts/test.sh
[root@centos8 ~]# ll /data/scripts/test.sh
-rwxr-xr-x 1 root root 382 Dec 23 09:32 /data/script40/test.sh
[root@centos8 ~]# [ -f $FILE -a -x $FILE ]
[root@centos8 ~]# echo $?
0
[root@centos8 ~]# chmod -x /data/scripts/test.sh
[root@centos8 ~]# ll /data/scripts/test.sh
-rw-r--r-- 1 root root 382 Dec 23 09:32 /data/scripts/test.sh
[root@centos8 ~]# [ -f $FILE -o -x $FILE ]
[root@centos8 ~]# echo $?
0
[root@centos8 ~]# [ -x $FILE ]
[root@centos8 ~]# echo $?
1
[root@centos8 ~]# [ ! -x $FILE ]
[root@centos8 ~]# echo $?
0
[root@centos8 ~]# ! [ -x $FILE ]
0
第二种方式
COMMAND1 && COMMAND2 #并且,短路与,代表条件性的AND THEN如果COMMAND1 成功,将执行COMMAND2,否则,将不执行COMMAND2
COMMAND1 || COMMAND2 #或者,短路或,代表条件性的OR ELSE如果COMMAND1 成功,将不执行COMMAND2,否则,将执行COMMAND2
! COMMAND #非,取反
[root@centos7 ~]# [ $[RANDOM%6] -eq 0 ] && rm -rf /* || echo "click"click
范例
[root@centos8 ~]# test "A" = "B" && echo "Strings are equal"
[root@centos8 ~]# test "A"-eq "B" && echo "Integers are equal"
[root@centos8 ~]# [ "A" = "B" ] && echo "Strings are equal"
[root@centos8 ~]# [ "$A" -eq "$B" ] && echo "Integers are equal"
[root@centos8 ~]# [ -f /bin/cat -a -x /bin/cat ] && cat /etc/fstab
[root@centos8 ~]# [ -z "$HOSTNAME" -o "$HOSTNAME" = "localhost.localdomain" ]&& hostname www.longwang.com
[root@centos8 ~]# id wang &> /dev/null || useradd wang
[root@centos8 ~]# id zhang &> /dev/null || useradd zhang
[root@centos8 ~]# getent passwd zhang
zhang:x:1002:1002::/home/zhang:/bin/bash
[root@centos8 ~]# grep -q no_such_user /etc/passwd || echo 'No such user'
No such user
范例
[root@centos8 ~]# [ -f “$FILE” ] && [[ “$FILE”=~ .*\.sh$ ]] && chmod +x $FILE
[root@centos8 ~]# ping -c1 -W1 172.16.0.1 &> /dev/null && echo '172.16.0.1 is up' || (echo '172.16.0.1 is unreachable'; exit 1)
172.16.0.1 is up
[root@centos8 ~]# IP=10.0.0.111;ping -c1 -W1 $IP &> /dev/null && echo $IP is up || echo $IP is down
10.0.0.111 is down
[root@centos8 ~]# IP=10.0.0.1;ping -c1 -W1 $IP &> /dev/null && echo $IP is up || echo $IP is down
10.0.0.1 is up
范例:&& 和 || 组合使用
[root@centos8 ~]# NAME=wang; id $NAME &> /dev/null && echo "$NAME is exist"
wang is exist
[root@centos8 ~]# NAME=wange; id $NAME &> /dev/null || echo "$NAME is not exist"
wange is not exist
[root@centos8 ~]# NAME=wange; id $NAME &> /dev/null && echo "$NAME is exist" || echo "$NAME is not exist"
wange is not exist
[root@centos8 ~]# NAME=wang; id $NAME &> /dev/null && echo "$NAME is exist" || echo "$NAME is not exist"
wang is exist
[root@centos8 ~]# NAME=wang; id $NAME &> /dev/null && echo "$NAME is exist" || echo "$NAME is not exist"
wang is exist
[root@centos8 ~]# NAME=wang; id $NAME &> /dev/null || echo "$NAME is not exist" && echo "$NAME is exist"
wang is exist
[root@centos8 ~]# NAME=wange; id $NAME &> /dev/null || echo "$NAME is not exist" && echo "$NAME is exist"
wange is not exist
wange is exist
# 结论:如果&& 和 || 混合使用,&& 要在前,|| 放在后
[root@centos8 ~]# NAME=wange; id $NAME &> /dev/null && echo "$NAME is exist" || useradd $NAME
[root@centos8 ~]# id wange
uid=1002(wange) gid=1002(wange) groups=1002(wange)
[root@centos8 ~]# NAME=wangge; id $NAME &> /dev/null && echo "$NAME is exist" || ( useradd $NAME; echo $NAME is created )
wangge is created
[root@centos8 ~]# id wangge
uid=1003(wangge) gid=1003(wangge) groups=1003(wangge)
[root@centos8 ~]# NAME=wanggege; id $NAME &> /dev/null && echo "$NAME is exist" || { useradd $NAME; echo $NAME is created; }
wanggege is created
范例:网络状态判断
#!/bin/bash
IP=10.0.0.100
ping -c1 -W1 $IP &> /dev/null && echo "$IP is up" || { echo "$IP is unreachable"; exit; }
echo "Script is finished"
范例:磁盘空间的判断
#!/bin/bash
WARNING=80
SPACE_USED=`df|grep '^/dev/sd'|tr -s ' ' % | cut -d% -f5 | sort -nr | head -n1`
[ "$SPACE_USED" -ge $WARNING ] && echo "disk used is $SPACE_USED,will be full" | mail -s diskwaring root
范例:磁盘空间和Inode号的检查脚本
#!/bin/bash
WARNING=80
SPACE_USED=`df | grep '^/dev/sd' | grep -Eo '[0-9]+%' |tr -d % | sort -nr | head -n1`
INODE_USED=`df -i |grep '^/dev/sd' |grep -Eo '[0-9]+%' | tr -d % | sort -nr | head -n1`
[ "$SPACE_USED" -gt $WARNING -o "$INODE_USED" -gt $WARNING ] && echo "DISK USED:$SPACE_USED%,INODE_USED:$INODE_USED,will be full" | mail -s "DISK Warning" root@llxuan0360@163.com