第三章:shell条件测试
为了能够正确处理Shell程序运行过程中遇到的各种情况,Linux Shell提供了一组测试运算符。通过这些运算符,Shell程序能够判断某种或者几个条件是否成立。条件测试在各种流程控制语句,例如判断语句和循环语句中发挥了重要的作用,所以,了解和掌握这些条件测试是非常重要的。
3.1 条件测试的基本语法
在Shell程序中,用户可以使用测试语句来测试指定的条件表达式的条件的真或者假。当指定的条件为真时,整个条件测试的返回值为0;反之,如果指定的条件为假,则条件测试语句的返回值为非0值。
格式1: test 条件表达式 test 数值比较 -eq test a -eq b
test 字符比较 a = b test a == b
[ ] 算数比较 -eq = ==但> <运算符有问题需要通过\转义符 -a -o !
[ ] 字符比较test支持= ==; 不支持-eq 测试符和测试值之间通过空格分割
[[ ]] && || 算数比较不需要转义符 [[ ]] 可以支持正则 =~
格式2: [ 条件表达式 ]
格式3: [[ 条件表达式 ]]
重点数值比较符通过字符类的测试测试
test | [ ] | [[ ]] ---支持正则符 | |
---|---|---|---|
数值比较 | test 1-eq 2 (-eq -gt -lt -ge -le -ne) | -eq -gt -lt -ge -le -ne -a -o ! | -eq -gt -lt -ge -le -ne && || ! --测试符都支持 |
字符比较 | test a = b 或者 test a == b -a -o ! | = == -a -o ! | = == && || ! a =~ ".*" a == a |
注意:[]的左右要有空格。
3.2 文件测试运算符
操作符 | 说明 | 举例 |
---|---|---|
-b file | 检测文件是否是块设备文件,如果是,则返回 true。 | [ -b $file ] 返回 false。 |
-c file | 检测文件是否是字符设备文件,如果是,则返回 true。 | [ -c $file ] 返回 false。 |
-d file | 检测文件是否是目录,如果是,则返回0;否则,返回1。 | [ -d $file ] 返回 false。 |
-f file | 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 0;否则,返回1。 | [ -f $file ] 返回 true。 |
-S | 是否为socket文件 | |
-p | 是否为管道符文件 | |
-L | 是否为链接文件 | |
-u | 是否有suid的权限 | |
-g file | 检测文件是否设置了 SGID 位,如果是,则返回 true。 | [ -g $file ] 返回 false。 |
-k file | 检测文件是否设置了冒险位(Sticky Bit),如果是,则返回 true。 | [ -k $file ] 返回 false。 |
-s file | 检测文件是否为空(文件大小是否大于0),不为空返回 true。 | [ -s $file ] 返回 true。 |
-e file | 检测文件(包括目录)是否存在,如果是,则返回 true。 | [ -e $file ] 返回 true。 |
-r file | 检测文件是否可读,如果是,则返回 true。 | [ -r $file ] 返回 true。 |
-w file | 检测文件是否可写,如果是,则返回 true。 | [ -w $file ] 返回 true。 |
-x file | 检测文件是否可执行,如果是,则返回 true。 | [ -x $file ] 返回 true。 |
普通文件 (Regular File):
表示:-
说明:这是最常见的文件类型,用于存储数据,比如文本文件、图片、可执行文件等。
目录文件 (Directory):
表示:d
说明:目录文件用于存储文件和子目录的路径信息,类似于Windows中的文件夹。
符号链接文件 (Symbolic Link):
表示:l
说明:符号链接文件类似于Windows中的快捷方式,它指向另一个文件或目录。
块设备文件 (Block Device):
表示:b
说明:块设备文件通常用于存储设备,如硬盘分区、CD-ROM等。这些设备以块为单位进行读写。
字符设备文件 (Character Device):
表示:c
说明:字符设备文件通常用于串行端口和其他按字符处理的I/O设备,如键盘、鼠标等。
命名管道文件 (Named Pipe):
表示:p
说明:命名管道文件用于进程间的通信,允许一个进程将数据写入管道,另一个进程从管道中读取数据。
套接字文件 (Socket):
表示:s
说明:套接字文件用于网络通信,它允许不同主机之间的进程进行通信。
在Linux中,你可以使用ls -l命令来查看文件和目录的详细信息,包括文件类型。例如:
ls -l
输出示例:
-rw-r--r-- 1 user group 4096 Oct 4 12:34 example.txt # 普通文件
drwxr-xr-x 2 user group 4096 Oct 4 12:34 example_dir # 目录文件
lrwxrwxrwx 1 user group 12 Oct 4 12:34 link_to_file -> example.txt # 符号链接文件
每一行的第一个字符表示文件的类型,后面的信息包括文件的权限、链接数、所有者、所属组、文件大小和修改时间等。
示例:
//普通文件测试
[root@localhost ~]# touch file1.txt
[root@localhost ~]# [ -f file1.txt ] && echo yes || echo no
yes
//目录测试
[root@localhost ~]# [ -d /tmp ] && echo yes || echo no
yes
//测试文件属性
[root@localhost ~]# ll file1.txt
-rw-r--r-- 1 root root 0 8月 28 12:30 file1.txt
[root@localhost ~]# [ -r file1.txt ] && echo yes || echo no
yes
[root@localhost ~]# [ -x file1.txt ] && echo yes || echo no
no
[ -e ]
[ -s ]
练习题:
对系统中所有日志文件备份
https://www.jianshu.com/p/382c0d06d13c
判断一个文件是文本文件还是目录文件:
bash
[root@localhost day03]# vim 2.sh
[root@localhost day03]# bash 2.sh
请输入一个文件名(绝对路径表示):/day03
/day03是目录文件
[root@localhost day03]# cat 2.sh
#!/bin/bash
# 要求用户输入一个已存在的文件名(绝对路径表示):判断文件是目录文件还是文本文件
read -p "请输入一个文件名(绝对路径表示):" a
([ -f $a ] && echo $a是文本文件) || ([ -d $a ] && echo $a是目录文件)
3.3 字符串运算符
示例:
bash
//-n如果字符串长度不为零输出yes,否则输出no
[root@localhost ~]# [ -n "hello" ] && echo yes || echo no
yes
[root@localhost ~]# [ -n "" ] && echo yes || echo no
no
注意://变量为空时通过[[]]进行测试,[]测试有问题
[root@localhost ~]# str=`grep xixi /etc/passwd`
[root@localhost ~]# [[ -n $str ]] && echo 有 || echo 无
无
[root@localhost ~]# [ -n $str ] && echo 有 || echo 无
有
//-z如果字符串长度为零输出yes,否则输出no
[root@localhost ~]# [ -z "hello" ] && echo yes || echo no
no
[root@localhost ~]# [ -z "" ] && echo yes || echo n
yes
// 字符串相等比较
注意:=的左右有空格,没有空格将会导致逻辑错误。
[root@localhost ~]# [ "HELLO" = "hello" ] && echo yes || echo no
no
[root@localhost ~]# [ "HELO" != "hello" ] && echo yes || echo no
yes
if [ -z $a ]
then
echo "-z $a : 字符串长度为 0"
else
echo "-z $a : 字符串长度不为 0"
fi
if [ -n "$a" ]
then
echo "-n $a : 字符串长度不为 0"
else
echo "-n $a : 字符串长度为 0"
fi
if [ $a ] -------$判断z字符串是否为空,不空为真,空为假
then
echo "$a : 字符串不为空"
else
echo "$a : 字符串为空"
fi
系统服务脚本案例:
bash
[root@localhost ~]# sed -n '30,31p' /etc/init.d/network
Check that networking is up.
[ "${NETWORKING}" = "no" ] && exit 6
3.3 关系运算符
bash
# 数值测试:
[root@localhost day03]# [ 1 -eq 2 ] && echo yes || echo no
no
[root@localhost day03]# test 1 -eq 2 && echo yes || echo no
no
[root@localhost day03]# [[ 1 = 2 ]] && echo yes || echo no
no
[root@localhost day03]# [[ 1 -eq 2 ]] && echo yes || echo no
no
[root@localhost day03]# ((1>=2)) && echo yes || echo no
no
# 字符串比较
[root@localhost day03]# [[ "a" == "b" ]]
[root@localhost day03]# echo $?
1
示例:
[root@localhost ~]# [ 5 -gt 3 ] && echo yes || echo no
yes
[root@localhost ~]# [ `id -u` -eq 0 ] && echo admin || echo other
admin
[root@localhost ~]# su - student
[student@localhost ~]$ [ `id -u` -eq 0 ] && echo admin || echo other
other
面试:判断输入是否是数字
#(1)grep过滤判断是否是字符串
read -p "请输入:" str
echo $str | egrep '^[a-zA-Z]+$'
[ $? -eq 0 ] && echo "您输入的是字符串" || echo "输入的不是字符串"
#(2)字符串运算 =~ A=~B 子串A是否包含子串B
[[ "$str" =~ [a-zA-Z]* ]] && echo "2您输入的是字符串" || echo "2输入的不是字符串"
#(3)利用通配符,判断指定输入字符是否在字符串中包含
[[ $str = *[a-zA-Z]* ]] && echo 包含字符 ||echo 不包含
#(4)利用case语句
case $str in
*[a-zA-Z]*)
echo 是字符串
;;
* )
echo 不是字符串类型
;;
esac
#(5)通过算术运算
expr $str "+" &>/dev/null
[ $? -eq 0 ] && echo 输入的是数字 || echo 输入的是字符
(6)a=1234;echo "$a"|[ -n "`sed -n '/^[0-9][0-9]*$/p'`" ] && echo string a is numbers
if [ -n "$(echo $1| sed -n "/^[0-9]\+$/p")" ];then
echo "$1 is number."
else
echo 'no.'
fi
[root@localhost ~]# num10=123
[root@localhost ~]# num20=ssss1114ss
[root@localhost ~]# [[ "$num10" =~ ^[0-9]+$ ]];echo $?
0
[root@localhost ~]# [[ "$num20" =~ ^[0-9]+$ ]];echo $?
1
#判断输入的字符是否为整数
## 方法1
a=1234;echo "$a"|[ -n "`sed -n '/^[0-9]*$/p'`" ] && echo string a is numbers
第一个-n是shell的测试标志,对后面的串"`sed -n '/^[0-9][0-9]*$/p'`" 进行测试,如果非空,则结果为真。
sed默认会显示所有输入行信息的,sed 的"-n"选项是让sed不要显示,而只显示我们所需要的内容:即后面的表达式所匹配的行,这是通过表达式中加入"p"命令来实现的。
/^[0-9][0-9]*$/他的含义是匹配至少由一位数字构成的行
## 方法2, 可以,不过不是bash实现的,是使用了grep的正则 -->grep的正则表达式,<<< 就是将后面的内容作为前面命令的标准输入
#if grep '^[[:digit:]]*$' <<< "$1";then
# echo "$1 is number."
#else
# echo 'no.'
#fi
## 方法3
#if [ "$1" -gt 0 ] 2>/dev/null ;then
# echo "$1 is number."
#else
# echo 'no.'
#fi
## 方法4,case
#case "$1" in
# [1-9][0-9]*)
# echo "$1 is number."
# ;;
# *)
# ;;
#esac
## 方法5,awk
#echo $1| awk '{print($0~/^[-]?([0-9])+[.]?([0-9])+$/)?"number":"string"}'
## 方法6,expr
expr $1 "+" 10 &> /dev/null
if [ $? -eq 0 ];then
echo "$1 is number"
else
echo "$1 not number"
fi
3.4 逻辑操作符/布尔运算符
运算符 | 说明 布尔符 | 举例 |
---|---|---|
&& | 逻辑的 AND -a | [[ $a -lt 100 && $b -gt 100 ]] 返回 false |
|| | 逻辑的 OR -o | [[ $a -lt 100 || $b -gt 100 ]] 返回 true |
! | 非 ! | not,非,两端相反,则结果为真 |
示例:
[root@localhost ~]# [ -f /etc/hosts -a -f /etc/services ] && echo yes || echo no
yes
[root@localhost ~]# [[ -f /etc/hosts && -f /etc/services ]] && echo yes || echo no
yes
总结: 1)算数运算符
2)文件测试
3)字符串运算符 -n
4)关系型运算符
5)逻辑运算符
3.5 命令执行顺序;
复合指令:即一串命令。
()和{}都是对一串的命令进行执行,但有所区别:
相同点: ()和{}都是把一串的命令放在括号里面,如果命令在一行命令之间用;号隔开
()和{}中括号里面的某个命令的重定向只影响该命令,但括号外的重定向则影响到括号里的所有命令
不同点 :
()只是对一串命令重新开一个子shell进行执行, {}对一串命令在当前shell执行
()最后一个命令可以不用分号,{}最后一个命令要用分号
()里的第一个命令和左边括号不必有空格,{}的第一个命令和左括号之间必须要有一个空格
[root@localhost day03]# (cd;pwd;hostname)
/root
localhost.localdomain
[root@localhost day03]# { cd;pwd;hostname; }
/root
localhost.localdomain
[root@localhost ~]#
示例1:
[root@localhost scripts]# (pwd;cd /tmp;pwd)
/scripts
/tmp
# ()子shell中执行,执行完毕,当前路径不变
[root@localhost tmp]# { pwd;cd /tmp;pwd; } > aaa
/tmp
/tmp
[root@localhost tmp]# pwd;cd /;pwd
示例2:
// 如果目录/abc存在给出提示信息目录已存在,否则创建目录
[ -e /abc -a -d /abc ]
方法1:[root@localhost ~]# [ -d /abc ] && echo exists || mkdir /abc
方法2:[root@localhost ~]# [ ! -d /abc ] && mkdir /abc || echo exists
3.6案例分析
示例1:判断当前已登录的用户数,当超过五个时输出"Too many"。
分析:1)如何查看当前登录用户--> who
2)已登录的用户数--> who | wc -l
num=$(who | wc -l)
[ $num -gt 5 ] && echo "Too many"
示例2:如果在/home/pushmail目录下不存在leadtone目录,则创建该目录
path=/home/pushmail/leadtone
[ -d $path ] || mkdir -p $path
或 [ ! -d $path ] && mkdir -p $path
示例3:判断数字大于500则执行big.sh 小于等于500则退出脚本,并输出报错信息exit
read -p "please input num: " n
[ $n -gt 500 ] && echo "big.sh" || (echo "小于等于500";exit 3) ----()用于复合指令
示例4:判断当前系统的语言环境,若不是"en_US"时输出提示信息"Not en_US"。
分析:1)如何获取当前系统语言环境--> LANG
lang=$LANG
# echo $lang
zh_CN.utf8
文件:/etc/locale.conf
2)处理字符串
方法1:echo $lang | cut -d. -f1
方法2:echo $lang | awk -F. '{print $1}'
方法3: lang=${LANG%.*} echo $lang
lang=$(echo $LANG | cut -d. -f1)
[ "$lang" = "en_US" ] || echo "Not en.US"
或[ "$lang" != "en_US" ] && echo "Not en.US"
# echo $lang
zh_CN.utf8
示例5:用Shell编程,判断一文件是不是目录文件,如果是将其拷贝到 /dev 目录下
分析:-c 判断是否是字符设备文件
cp -i
[ $# -ne 1 ] && (echo "no input file...";exit 4)
[ -c "$1" ] && \cp $1 /dev
示例6:通过ping百度来测试是否能上网
分析:通过命令的返回值来判断ping -i 5 localhost 每隔五秒显示
ping -c 2 www.baidu.com &>/dev/null && echo up || echo down
[ $? -eq 0 ] && echo "net is up..."
面试题:写一个shell脚本来看看你使用最多的命令是哪些,列出你最常用的命令top10。
分析:命令保存文件 /root/.bash_history
# sort /root/.bash_history | uniq -c | sort -nr | head
[root@localhost 桌面]# history | tr -s " "| cut -d " " -f 3- | sort | uniq -c | sort -rn | head
dirname和basename:
[root@localhost day03]# file=/test/1.txt
[root@localhost day03]# dirname $file
/test
[root@localhost day03]# basename $file
1.txt
练习:
bash
1、取出/etc/passwd文件的第6行;
2、取出当前系统上所有用户的shell,要求,每种shell只显示一次;
使用cut、sort结合管道实现
3、如果/var/log/messages文件的行数大于100,就显示好大的文件
4、显示/etc目录下所有以pa开头的文件,并统计其个数;ls /etc | grep ^pa* | wc -l
5、如果用户hadoop不存在就添加,否则显示用户已存在
6、编写一个 Shell 程序 mkf,此程序的功能是:显示 root 下的文件信息,然后建立一个 kk 的文件夹,在此文件夹下建立一个文件 aa,修改此文件的权限为可执行
7、编写一个 Shell 程序 test3,程序执行时从键盘读入一个目录名,然后 显示这个目录下所有文件的信息
8、编写一个 Shell 程序 test4,从键盘读入 x、y 的值,然后做加法运算,最后输出结果
写一个脚本,完成以下要求:
给定一个用户:
1、如果其UID为0,就显示此为管理员;
2、否则,就显示其为普通用户;
# 练习:写一个脚本
# 判断当前系统上是否有用户的默认shell为bash;
# 如果有,就显示有多少个这类用户;否则,就显示没有这类用户;
[root@localhost day03]# vim lianxi.sh
bash=$(grep -c '/bin/bash$' /etc/passwd)
[ $bash -gt 0 ] && echo "有 $bash 个用户的默认shell为bash" || echo "没有用户的默认shell为bash"
[root@localhost day03]# bash lianxi.sh
有 4 个用户的默认shell为bash
# 练习:写一个脚本
# 给定一个文件,比如/etc/inittab;
# 判断这个文件中是否有空白行;
# 如果有,则显示其空白行数;否则,显示没有空白行。
[root@localhost day03]# vim lianxi2.sh
file="/etc/inittab"
[[ $kong -gt 0 ]] && echo "文件 $file 中有 $kong 个空白行" || echo "文件 $file 中没有空白行"
[root@localhost day03]# bash lianxi2.sh
文件 /etc/inittab 中没有空白行
# 练习:写一个脚本
# 给定一个用户,判断其UID与GID是否一样;
# 如果一样,就显示此用户为"good guy";否则,就显示此用户为"bad guy"
[root@localhost day03]# vim lianxi3.sh
read -p "请输入一个用户名:" a
uid=$(id -u "$a")
gid=$(id -g "$a")
[ $? != 0 ] && echo "用户 $a不存在" && exit a
[ "$uid" == "$gid" ] && echo "用户 $a 是 good guy" || echo "用户 $a 是 bad guy"
[root@localhost day03]# bash lianxi3.sh
请输入一个用户名:zhou
用户 zhou 是 good guy