1、条件判断语法结构
1.1 条件判断语法格式
格式1:test 条件表达式
格式2:[ 条件表达式 ]
格式3:[[ 条件表达式 ]] 支持正则 =~
特别说明:[ ] 和 [[ ]] 两边都有空格
更多判断可以采用man test 去查看,很多参数都用来进行条件判断
有空格和无空格区别:
[条件表达式] #无空格
[ 条件表达式 ] #有空格
[[条件表达式]] #无空格
[[ 条件表达式 ]] #有空格
1.2 条件判断相关参数(常用)
判断文件类型
判断参数 | 含义 |
---|---|
-e | 判断文件是否存在(任何类型文件) |
-f | 判断文件是否存在并且是一个普通文件 |
-d | 判断文件是否存在并且是一个目录 |
-L | 判断文件是否存在并且是一个软连接文件 |
-b | 判断文件是否存在并且是一个块设备文件 |
-S | 判断文件是否存在并且是一个套接字文件 |
-c | 判断文件是否存在并且是一个字符设备文件 |
-p | 判断文件是否存在并且是一个命名管道文件 |
-s | 判断文件是否存在并且是一个非空文件(有内容) |
[ -d ./test1 ];echo $? #判断当前目录下是否存在名为test1的目录,如果存在,打印结果为0
[ -L ./test2 ];echo $? #判断当前目录下是否存在名为test1的软链接,如果存在,打印结果为0
[ ! -f ./test2 ];echo $? #如果当前目录下不存在名为test2的普通文件,则打印0
判断文件权限
判断参数 | 含义 |
---|---|
-r | 当前用户对其是否可读 |
-w | 当前用户对其是否可写 |
-x | 当前用户对其是否可执行 |
-u | 是否有suid,高级权限冒险位 |
-g | 是否sgid,高级权限强制位 |
-k | 是否有t位,高级权限粘滞位 |
判断文件新旧
说明:这里的新旧指的是文件的修改时间
判断参数 | 含义 |
---|---|
file1 -nt file2 | 比较file1是否比file2新 |
file1 -ot file2 | 比较file1是否比file2旧 |
file1 -ef file2 | 比较是否为同一个文件,或者用于判断硬链接是否指向同一个inode |
判断整数
判断参数 | 含义 |
---|---|
-eq | 相等 |
-ne | 不等 |
-gt | 大于 |
-lt | 小于 |
-ge | 大于等于 |
-le | 小于等于 |
判断字符串
判断参数 | 含义 |
---|---|
-z | 判断是否为空字符串,字符串长度为0则成立 |
-n | 判断是否为非空字符串,字符串长度不为0则成立 |
string1 = string2 | 判断字符串是否相等 |
string1 != string2 | 判断字符串是否相不等 |
多重条件判断
判断符号 | 含义 | 举例 |
---|---|---|
-a 和 && | 逻辑与 | [ 1 -eq 1 -a 1 -ne 0 ] [ 1 -eq 1 ] && [ 1 -ne 0 ] |
-o 和 || | 逻辑或 | [ 1 -eq 1 -o -ne 1 ] [ 1 -eq 1 ] |
特别说明:
前面的表达式为真,才会执行后面的代码
前面的表达式为假,才会执行后面的代码
只用于分割命令或表达式
&& 和 || 没有优先级,从左往右依次计算
数值比较
[ $(id -u) -eq 0 ] && echo "admin" #如果当前用户为root,则打印admin
[ $(id -u) -eq 0 ] || echo "user" #如果当前用户不为root,则打印user
类C风格比较
注意:在(())中,=表示赋值,==表示判断
((1==2));echo $?
((1<2));echo $?
((2>=1));echo $?
((2!=1));echo $?
((`id -u`==0));echo $?
((a=123));echo $a
unset a
((a==123));echo $?
字符串比较
a="hello world";b="world"
[ $a = $b ];echo $?
[ "$a" = "$b" ];echo $?` #判断字符串相等
[ "$a" != "$b" ];echo $?
[ "$a" !== "$b" ];echo $? #错误写法
[ "$a" == "$b" ];echo $? #判断字符串相等
test "$a" != "$b";echo $?
[ ] 和 [[ ]]的区别
区别1:
[root@fl ~]# a=
[root@fl ~]# [ $a = hello ];echo $?
bash: [: =: unary operator expected
2
[root@fl ~]# [ $a = "hello" ];echo $?
bash: [: =: unary operator expected
2
[root@fl ~]# [ "$a" = "hello" ];echo $?
1
[root@fl ~]# [[ $a = hello ]];echo $?
1
区别2:
[root@fl Shell]# [ -e test1.sh && 1 -eq 1 ];echo "正确"
bash: [: missing `]'
正确
[root@fl Shell]# [[ -e test1.sh && 1 -eq 1 ]];echo "正确"
正确
总的来说:[[ ]] 还支持逻辑操作符 && 和 ||,支持正则表达式,支持整数比较符(=、>、<、>=、<=),而[ ] 不支持
2、流程控制语句
2.1 基本语法结构
if结构
if [ condition ];then
command
fi
if test conditon;then
command
fi
if [[ condition ]];then
command
fi
[ 条件 ] && command
if…else结构
if [ condition ];then
command1
else
command2
if
[ 条件 ] && command1 || command2
案例:让用户自己输入字符串,如果输入"hello",则打印"world",否则打印"请输入hello"
#!/bin/env bash
read -p "请输入一个字符串" str
if [ "$str" = "hello" ];then
echo "world"
else
echo "请输入hello"
fi
if…elif…else结构
if [ condition1 ];then
command1 #结束
elif [ condition2 ];then
command2 #结束
else
command3 #结束
fi
2.2 应用案例
案例1:判断当前主机是否和远程主机是否ping通
#!/bin/env bash
# 用户输入对端主机
read -p "请输入对端主机IP:" ip
# ping 10次
ping -c 10 ${ip} &>/dev/null #/dev/null是一个黑洞文件,往里面写的数据都将被丢弃
# 判断是否互通
if [ $? -eq 0 ];then
echo "当前主机和远程主机${ip}是互通的"
else
echo "当前主机和远程主机${ip}不是互通的"
fi
案例2:判断web服务器中的httpd进程是否存在
# 方法1
ps -axj | grep httpd | grep -v "grep" &>/dev/null # grep -v "grep" 表示除去当前的grep进程
if [ $? -eq 0 ];then
echo "httpd进程存在"
else
echo "httpd进程存在"
fi
# 方法2
pgrep httpd &>/dev/null
if [ $? -eq 0 ];then
echo "httpd进程存在"
else
echo "httpd进程存在"
fi
# 或者
test $? -eq 0 && echo "httpd进程存在" || echo "httpd进程不存在"
pgrep命令
以名称为依据从运行进程队列中查找进程,并显示查找到的进程id
选项:
-o: 仅显示找到的最小(起始)进程号;
-n: 仅显示找到的最大(结束)进程号;
-l: 显示进程名称;
-P: 指定父进程号; pgrep -p 4764 查看父进程下的子进程id
-g: 指定进程组;
-t: 指定开启进程的终端;
-u: 指定进程的有效用户ID
案例3:判断门户网站是否能正常访问
#!/bin/env bash
# wget命令用于从一个网站中下载文件,如果能下载成功,则表示该网站能正常访问
web_server=www.baidu.com
wget -P /dev/null ${web_server} &>/dev/null #-P 表示下载文件的保存路径
if [ $? -eq 0 ];then
echo "能正常访问"
else
echo "不能正常访问"
fi
3、循环语句
列表for循环
for variable in {list}
do
command
command
...
done
或者
for variable in a b c
do
command
command
...
done
#打印1-10 以下写法结果一样
for i in {1..10}; do echo &i; done
seq 10
for i in $(seq 10); do echo $i; done
for i in 1 2 3 4 5 6 7 8 9 10; do echo $i; done
#打印1 3 5 7 9 以下写法结果一样
for i in{1..10..2}; do echo $i; done
seq 1 2 10
#打印10-1 以下写法结果一样
for i in {1..10..-1}; do echo $i; done
seq 10 -1 1
不带列表循环
不带列表的for循环执行时由用户指定参数和参数的个数
for variable
do
command
command
...
done
举例说明
#!/bin/env bash
for i #只有一个i,没有范围
do
echo "hello"
done
[root@fl Shell]# bash test8.sh
[root@fl Shell]# bash test8.sh a
hello
[root@fl Shell]# bash test8.sh a b
hello
hello
# 为什么要带参数才能有执行结果
[root@fl Shell]# bash -x test8.sh a #会将其解释为以下结果
+ for i in '"$@"' # "$@"表示脚本后面的所有参数,因此有几个参数就循环几次
+ echo hello
hello
类C风格的for循环
for(( expr1;expr2;expr3 ))
do
command
command
...
done
for(( i=1;i<=5;i++))
do
echo $i
done
expr1:定义变量并赋初值
expr2:决定是否进行循环(条件)
expr3:决定循环变量如何改变,决定循环什么时候退出
循环体:do…done之间的内容
- continue:结束当前循环,进行下次循环
- break:跳出本次循环
- exit:直接退出程序
- shift:使位置参数向左移动,默认移动1位,可以使用shift 2
shift案例:
[root@fl Shell]# cat test15.sh
#!/bin/env bash
sum=0
while [ $# -ne 0 ]
do
let sum=$sum+$1
shift
done
echo sum=$sum
[root@fl Shell]# bash test15.sh 1 2 3 4 5
sum=15
在未运行shift命令之前$1是可用的,当使用shift命令之后,原来的$2会变成$1,并且原有的$1变得不可用,通过$#命令获得的参数个数也会少1
案例4:批量创建用户
批量加5个新用户,以u1到u5命名, 并统一加一个新组,组名为class,统一改密码为123
#!/bin/env bash
#判断class组是否存在
grep -w ^class /etc/group &>/dev/null
#如果不存在就创建class组
if [ $? -ne 0 ];then
groupadd class
#循环创建用户
for i in {1..5}
do
useradd -G class u$i #创建用户
echo 123 | passwd --stdin u$i &>/dev/null #设置密码
done
案例5:写一个脚本,局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里
#!/bin/env bash
if [ ! -d `pwd`/tmp ];then
mkdir `pwd`/tmp
fi
ip=10.1.1
for((i=1;i<=10;++i))
do
ping -c1 $ip.$i &>/dev/null
if [ $? -eq 0 ];then
echo "$ip.$i ping is ok">>./tmp/ip_up.txt
else
echo "$ip.$i ping is down">>./tmp/ip_down.txt
fi
done
echo "局域网内IP检测完毕"
这样存在一个问题,每次需要等待前一个ping完成后,才能进行下一次ping,并且是前台运行,很耗时,因此可以对代码进行改进
#!/bin/env bash
if [ ! -d `pwd`/tmp ];then
mkdir `pwd`/tmp
fi
ip=10.1.1
for((i=1;i<=10;++i))
do
{
ping -c1 $ip.$i &>/dev/null
if [ $? -eq 0 ];then
echo "$ip.$i ping is ok">>./tmp/ip_up.txt
else
echo "$ip.$i ping is down">>./tmp/ip_down.txt
fi
}&
done
wait
echo "局域网内IP检测完毕"
并发执行:
{程序}& 表示将程序放到后台并发执行,如果需要等待程序执行完毕再进行后续操作,需要加上wait
while循环
条件为真进入循环,条件未假结束循环
while 表达式
do
command...
done
while [ 1 -eq 1] 或者 (( 1 < 2 ))
do
command...
done
案例6:同步系统时间
需求:
写一个脚本,30秒同步一次系统时间,时间同步服务器www.baidu.com
如果同步失败,则进行邮件报警,每次失败都报警
同步成功,也进行邮件通知,但是成功100次才通知一次
#!/bin/env bash
NTP=www.baidu.com
count=0
while true
do
ntpdate $NTP &>/dev/null
if [ $? -ne 0 ];then
echo "system date failed" | mail -s "check system date" root@localhost
else
let count++
if [ $count -eq 100 ];then
echo "sysyem date success" | mail -s "check system date" root@localhost && count=0
fi
fi
sleep 30
done
# ntpdate $NTP 本机同步ip地址为NTP的服务器时间 也可换成 rdate -s
# mail -s "check system date" root@localhost 向root发送邮件
until循环
条件未假就进入循环,条件为真就结束循环
untile expreesion
do
command...
done