Shell学习笔记(2)——if-then和case语句

前言

本篇笔记的参考资料是(《Linux命令行与shell脚本编程大全》(第3版),外加百度辅助,本篇笔记的主要内容是if-then和case语句。

许多程序要求对shell脚本中的命令添加一些逻辑流控制。有一类命令会根据条件使脚本跳过某些命令,这样的命令叫做结构化命令(structured command)。

if-then语句

if-then语句是最基本的结构化命令,if-then的结构如下所示:

1
2
3
if commandA
then commandB
fi

解释:if后面跟一个shell命令commandA,如果commandA命令的退出状态码是0(表示此命令成功运行),位于then后面的commandB命令就会被执行,如果commandA命令的退出状态码是其他值,then后面的commandB就不会被执行,而是继续执行脚本中的下一具命令,fi语句用来表示if-theny语句到此结构。

第1案例:简单的if-then

1
2
3
4
5
6
#!/bin/bash
# testing the if statement
if pwd
then
echo "It worked"
fi

运行结果如下所示:

1
2
3
[20161111.DESKTOP-IVG4HR1] ➤ bash test1.sh
/home/mobaxterm
It worked

代码解释:if部分用了pwd命令,这个命令运行成功后,echo就会显示后面的字符串。

第2案例:出错的if-then

看一段代码,如下所示:

1
2
3
4
5
#!/bin/bash
# testing a bad command
if IamNotaCommand;then
echo "It worked"
fi

运行后,结果如下:

1
2
3
[2018-04-28 10:41.15] ~
[20161111.DESKTOP-IVG4HR1] ➤ bash test2.sh
test2.sh: line 3: IamNotaCommand: command not found

代码解释:if后面跟的是一个无法工作的命令,它会产生一个非零的退出状态码,bash shell会跳过then部分。

第3案例:then部分的多条命令

在if-then语句中,then部分可以输入多条命令,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash
# testing multiple commands in the then seciton
#
testuser=biotest
#
if grep $testuser /etc/passwd
then
echo "This is my first command"
echo "This is my second command"
echo "I can even put in other commands besides echo: "
ls -a /home/$testuser/.b*
fi

运行后,结果如下:

1
2
3
4
5
6
biotest@ubuntu:~/test$ bash test3.sh
biotest:x:1000:1000:UBUNTU,,,:/home/biotest:/bin/bash
This is my first command
This is my second command
I can even put in other commands besides echo:
/home/biotest/.bash_history /home/biotest/.bash_logout /home/biotest/.bashrc

代码解释:if grep $testuser /etc/passwd这条代码用于查找某个用户名当前是否在系统上使用,如果有用户使用了这个登录名,脚本会显示出一些文本信息,并列出该用户HOME目录中的bash文件。如果没有这个用户,就什么都不显示,现在将testuser=biotest改为testuser=biotest0,运行,如下所示:

1
2
biotest@ubuntu:~/test$ bash test3.sh
biotest@ubuntu:~/test$

if-then-else语句

if-then使用的场景是:如果if后面的命令成功执行,就输出then后面的内容,如果无法成功执行,什么都不输出。只有一这一种情况。而if-then-else语句则是能够执行其他的命令,它的格式如下所示:

1
2
3
4
5
6
if commandA
then
commandB
else
commandC
fi

命令解释:当if语句中的commandA命令返回退出状态状码0时,then部分中的命令commandB会执行,到此if-then-else语句与if-then语句是相同的。如果if语句中的commandA命令返回非零状态码时,bash shell会执行else部分中的命令。看一个案例。

第1案例:常规if-then-else语句

看下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash
# testing the else section
#
testuser=biotest0
#
if grep $testuser /etc/passwd
then
echo "The bash files for user $testuser are: "
ls -a /home/$testuser/.b*
echo
else
echo "The user $testuser does not exist on this system."
echo
fi

运行结果如下所示:

1
2
biotest@ubuntu:~/test$ bash test4.sh
The user biotest0 does not exist on this system.

从代码与结果可以看出,if grep $testuser /etc/passwd命令出错,返回状态码是非0,then后面的命令不会执行,路过,执行else后面的命令。

嵌套if

第1个案例:常规if-then-else语句

案例:要检验/etc/passwd文件中是否存在某个用户名以及该用户的目录是否存在,可以使用嵌套的if-then语法,嵌套的if-then语句位于主if-then-else语句的else代码块中,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash
# testing nested ifs
#
testuser=NoSuchUser
#
if grep $testuser /etc/passwd
then
echo "The user $testuser exists on this system."
else
echo "The user $testuser does not exist on this system."
if ls -d /home/$testuser/
then
echo "However, $testuser has a directory."
fi
fi

运行结果,如下所示:

1
2
The user NoSuchUser does not exist on this system.
ls: cannot access '/home/NoSuchUser/': No such file or directory

程序解释:if grep $testuser /etc/passwd这个命令无法运行,状态返回码是非0,跳过了echo "The user $testuser exists on this system."这个语句,执行后面的语句,即"The user $testuser does not exist on this system.",接头是个嵌套语句,其中ls -d /home/$testuser/无法执行(因为不存在这个目录),then后面的 也无法执行。

从结果可以看出,NoSuchUser这个账户不存在,他的目录也不存在。if-then-else还有一个延伸,即elif,它的格式如下:

1
2
3
4
5
6
7
if commandA
then
commandB
elif commandC
then
more commands
fi

elif语句提供了一个要测序的命令,这类似于原始的if语句,如果elif后命令的退出状态码是0,bash就执行第二个then语句部分的命令,即more commands这一部分,看一个案例(提前在/home/目录下建立一个NoSuchUser目录):

第2个案例:elif语句

这是对第1个案例的修改,所实现的目的是一样的,只是这里用到了elif语句,这种语句在逻辑上更加清晰。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash
# Testing nested ifs=user elif
#
testuser=NoSuchUser
#
if grep $testuser /etc/passwd
then
echo "The user $testuser exists on this system."
#
elif ls -d /home/$testuser
then
echo "The user $testuser does not exist on this system."
echo "Howerve, $testuser has a directory."
#
fi

运行后,结果如下所示:

1
2
3
4
biotest@ubuntu:~/test$ bash test6.sh
/home/NoSuchUser
The user NoSuchUser does not exist on this system.
Howerve, NoSuchUser has a directory.

程序解释:if grep $testuser /etc/passwd这条语句无法执行,执行elif后面的语句,即ls -d /home/$testuser,这条语句可以执行(因为我们提前建立了/home/NoSuchUser这个目录),随后执行then后面的语句。

第3个案例:elif扩展

针对上面的案例,对脚本进行更改,让这个脚本的功能变成:一、检查拥有目录的不存在用户;二、检验没有目录的不存在用户,这是通过在嵌套elif中添加了一个else语句来实现的,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash
# Testing nested ifs - user elif & else
#
testuser=NoSuchUser
#
if grep $testuser /etc/passwd
then
echo "The user $testuser exists on this system."
#
elif ls -d /home/$testuser
then
echo "The user $testuser does not exist on this system."
echo "Howerve, $testuser has a directory."
#
else
echo "The user $testuser does not exist on this system."
echo "And,$testuser does not have a directory."
fi

第1次运行结果:

1
2
3
4
biotest@ubuntu:~/test$ bash test7.sh
/home/NoSuchUser
The user NoSuchUser does not exist on this system.
Howerve, NoSuchUser has a directory.

第2次运行结果,现在删除掉/home/NoSuchUser目录,运行结果如下所示:

1
2
3
4
5
biotest@ubuntu:~/test$ sudo rm -rf /home/NoSuchUser
biotest@ubuntu:~/test$ bash test7.sh
ls: cannot access '/home/NoSuchUser': No such file or directory
The user NoSuchUser does not exist on this system.
And,NoSuchUser does not have a directory.

从两次的运行结果可以看出,在删除/home/NoSuchUser目录之前,即第1次运行,脚本执行的是elif后面的语句,返回状态码是0,删除了目录之后,elif语句返回的是非零状态码,elif块中的else就开始执行。需要注意的是,在elif语句中,紧跟其后的else语句属于elif代码块,它们并不属于if-then代码块。

Test命令

test命令是shell环境中测试条件表达式的工具。在前面的案例中,if后面的语句都是普通的shell语句,无法测序命令退出状态码之外的条件。在test命令中,如果test后面列出的条件成立,test命令就会退出并返回退出状态码0,如果条件不成立,test命令就会退出并返回非零的退出状态码,这就会使if-then语句不会被再执行,test的语句格式如下所示:

1
test condition

其中,condition是test命令要测试的一系列参数和值,如果test与if-then配合使用的话,格式如下所示:

1
2
3
4
if test condition
then
commands
if

如果不写test命令的condition部分,它会以非零状态码退出,并执行else语句块,看一个案例。

第1案例:常规test:test不添加condition

测试一下常规的test功能,如下所示:

1
2
3
4
5
6
7
8
9
#!/bin/bash
# Testing the test command
#
if test
then
echo "No expression returns a True"
else
echo "No expression returns a False"
fi

结果如下:

1
2
biotest@ubuntu:~/test$ bash test8.sh
No expression returns a False

程序解释:test后面没有condition,因此它以非零状态码退出,执行else后面的语句,即echo "No expression returns a False",如果添加上condition,则是下面的案例:

第2案例:常规test:test添加condition

代码如下:

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
# testing the test command
#
my_variable="Full"
#
if test $my_variable
then
echo "The $my_variable expression returns a True"
else
echo "The $my_variable expressoion returns a False"
fi

结果如下所示:

1
2
biotest@ubuntu:~/test$ bash test9.sh
The Full expression returns a True

代码解释:test后面有内容(Full),因此当test命令测试条件时,返回的状态为0,执行then后面的语句。如果$my_variable=""时,没有内容,就会执行else后面的语句,就跟第1个案例一样了。

shell另外一种条件测试方法

shell还有另外一种条件测序方法,无需要在if-then语句中使用test命令,其格式如下所示:

1
2
3
4
if [ condition ]
then
commands
fi

[ condition ]中定义了测试条件,需要注意提方括号中,condition前后要加上空格,否则就会报错,test命令可以判断三类条件,分别为①数值比较;②字符串比较;③文件比较。

数值比较

test命令常见的情形是对两个数值进行比较,下表就列出了测试两个值可用的条件参数:

比较 描述
n1 -eq n2 检查n1是否与n2相等
n1 -ge n2 检查n1是否大于或等于n2
n1 -gt n2 检查n1是否大于n2
n1 -le n2 检查n1是否小于或等于n2
n1 -lt n2 检查n1是否小于n2
n1 -ne n2 检查n1是否不等于n2

第1案例:数值比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!bin/bash
# Using numeric test evaluations
#
value1=10
value2=11
#
if [ $value1 -gt 5 ] # test value1 is great 5 or not
then
echo "The test value $value1 is greater than 5"
fi
#
if [ $value1 -eq $value2 ] # test value1 and value2 is equal or not
then
echo "The values are equal"
else
echo "The values are different"
fi
#

结果如下:

1
2
3
biotest@ubuntu:~/test$ bash test10.sh
The test value 10 is greater than 5
The values are different

第2案例:浮点数的比较

如果涉及浮点比较时,数值条件测试会有一个限制,如下所示:

1
2
3
4
5
6
7
8
9
10
11
#!bin/bash
# Using floating point numbers in test evaluations
#
value1=5.555
#
echo "The test value is $value1"
#
if [ $value1 -gt 5 ]
then
echo "The test value $value1 is greater than 5"
fi

结果如下:

1
2
3
biotest@ubuntu:~/test$ bash test11.sh
The test value is 5.555
test11.sh: line 8: [: 5.555: integer expression expected

结果中提示有问题,因为变量value1中储存提浮点值,脚本对这个值进行了测试,因为bash shell只能处理整数,如果只是要通过echo语句来显示这个结果,没问题,但是如果是基于数字的函数,就会出现了问题,因此需要注意。

字符串比较

字符串的比较方法如下所示:

比较 描述
str1 = str2 检查str1是否和str2相同
str1 != str2 检查str1是否和str3不相同
str1 < str2 检查str1是否比str2小
str1 > str2 检查str1是否比str2大
-n str1 检查str1的长度是否非0
-z str1 检查str1的长度是否为0

字符串比较第1案例

代码如下;

1
2
3
4
5
6
7
8
#!bin/bash
# testing string equality
testuser=biotest
#
if [ $USER = $testuser ]
then
echo "Welcom $testuser"
fi

运行结果如下所示:

1
2
biotest@ubuntu:~/test$ bash test12.sh
Welcom biotest

字符串比较第2案例:判断字符串不等的条件

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
# testing string equality
testuser=baduser
#
if [ $USER != $testuser ]
then
echo "This is not $testuser"
else
echo "Welcome $testuser"
fi

结果如下所示:

1
2
biotest@ubuntu:~/test$ bash test13.sh
This is not baduser

字符串的大小

在比较字符串方面,有一些事情比较麻烦,主要有两个方面,①大于号和小于号必须要转义,否则shell会将它们当成重定向符号,把字符串值当作文件名;②大于和小于顺序和sort命令所采用的不同,看一个案例。

第1案例:大于或小于号的重定向问题

看下面的案例,注意大于号。

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash
# mis-using string comparisons
#
val1=baseball
val2=hockey
#
if [ $val1 > $val2 ]
then
echo "$val1 is greater than $val2"
else
echo "$val1 is less than $val2"
fi

结果如下所示:

1
2
3
4
5
biotest@ubuntu:~/test$ bash badtest.sh
val1 is less than val2
biotest@ubuntu:~/test$ ll
total 72
-rw-rw-r-- 1 biotest biotest 0 Apr 27 23:21 hockey

运行结果时,shell没有报错,但是给出的结果却是错的,因为脚本把大于事情解释成了输出重定向,它在当前目录下创建了一个名为hockey的文件,由于重定向的完成,test命令(就是if语句)返回了退出状态码0。

第2案例:解决字符串比较时,大于号的问题

如果要解决上述问题,需要对大于号进行转义,前面加上\符号即可,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash
# mis-using string comparisons
#
val1=baseball
val2=hockey
#
if [ $val1 \> $val2 ]
then
echo "$val1 is greater than $val2"
else
echo "$val1 is less than $val2"
fi

结果如下所示:

1
2
biotest@ubuntu:~/test$ bash badtest2.sh
baseball is less than hockey

sort命令对字符串的排序问题

在处理字符串方面,sort和test的方法正好相反, 看一个案例。

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
# testing astring osrt order
val1=Testing
val2=testing
#
if [ $val1 \> $val2 ]
then
echo "$val1 is greater than $val2"
else
echo "$val1 is less than $val2"
fi

运行结果如下所示:

1
2
biotest@ubuntu:~/test$ bash test14.sh
Testing is less than testing

结查要显示,Testing比testing小,如果是sort,则是下面的结果:

1
2
3
4
5
6
biotest@ubuntu:~/test$ cat testfile
Testing
testing
biotest@ubuntu:~/test$ sort testfile
testing
Testing

testing要比Testing大。

在比较测试上,大写字符被认为是小于小写字母的,但是在sort命令中,小写字母会先出现。这是因为比较测试中使用的是标准的ASCII顺序,根据每个字符的ASCII为当会来决定排序结果,而sort命令使用的也是ASCII顺序排序,只是默认情况下,sort会按照ASCII从小到大进行排序,如果加上了-r参数,则是按照默认排序的相反顺序进行排序,如下所示:

1
2
3
biotest@ubuntu:~/test$ sort -r testfile
Testing
testing

判断字符串是否为0

-n-z可以检查一个变量是否含有数据,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/bin/bash
# testing string length
val1=testing
val2=''
#
if [ -n $val1 ]
then
echo "The string '$val1' is not empty"
else
echo "The string '$val1' is empty"
fi
#
if [ -z $val2 ]
then
echo "The string '$val2' is empty"
else
echo "The string '$val2' is not empty"
fi
#
if [ -z $val3 ]
then
echo "The string '$val3' is empty"
else
echo "The string '$val3' is not empty"
fi

运行结果如下所示:

1
2
3
4
biotest@ubuntu:~/test$ bash test15.sh
The string 'testing' is not empty
The string '' is empty
The string '' is empty

程序解释:①先创建了2个字符串变量,val1包含一个字符串,val2包含的是一个空字符串;②if [ -n $val1 ]表示:判断val1的变量是否长度非0,因为val1并不是一个空字符串,它不是0,因此执行后面的then部分;③if [ -z $val2 ]:判断val2的变量是否长度为0,而val2是一个空字符串,它的长度为0,因此执行then部分;④if [ -z $val3 ]:判断val3的变量是否长度为0,由于这个变量并未在shell脚本中定义过,所以它的字符串长度仍然为0。

文件比较

文件比较是shell编程中使用比较多的形式,它允许用户测试Linux文件系统上的文件和目录的状态,下表列出了这些比较方式,如下所示:

比较 描述
-d file 检查file是否存在并是一个目录
-e file 检查file是否存在
-f file 检查file是否存在并 一个文件
-r file 检查file是否存在并可读
-s file 检查file是否存在并非空
-w file 检查file是否存在并可写
-x file 检查file是否存在并可执行
-O file 检查file是否存在并属当前用户所有
-G file 检查file是否存在并默认组与当前用户相同
file1 -nt file2 检查file是否比file2新
file1 -ot file2 检查file是否比file2旧

检查目录

-d测试会检查指定的目录是否存在于系统中如果打算将文件写入目录或是准备切换到某个目录,先进行测试是一种好习惯,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash
# Look before you leap
#
jump_directory=/home/arthur
#
if [ -d $jump_directory ]
then
echo "The $jump_directory directory exists"
cd $jump_directory
ls
else
echo "The $jump_directory directory does not exist"
fi
#

结果运行如下所示:

1
2
biotest@ubuntu:~/test$ bash directest.sh
The /home/arthur directory does not exist

代码解释:-d测试用于检测jump_directory变量是中目录是否存在,如果存在就使用cd命令进入该目录,如果不存在脚本就输出一条信息,然后退出。

检查对象是否存在

-e参数可以使脚本代码在使用文件或上当前检查它们是否存在,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/bash
# check if either a directory or file exists
#
location=$HOME
file_name="sentinel"
#
if [ -e $location ]
then # Directory does exist
echo "OK on the $location directory."
echo "Now checking on the file, $file_name."
#
if [ -e $location/$file_name ]
then # file does exist
echo "OK on the filename"
echo "Updating Current Date..."
date >> $location/$file_name
#
else # file does no exist
echo "File does not exist"
echo "Nothing to update"
fi
#
else #Directory does exist
echo "The $location directory does not exist."
echo "Nothing to update"
fi

第一次运行,结果如下所示:

1
2
3
4
5
biotest@ubuntu:~/test$ bash object_exist.sh
OK on the /home/biotest directory.
Now checking on the file, sentinel.
File does not exist
Nothing to update

第二次运行,如果创建了sentinel文件,则如下所示:

1
2
3
4
5
6
7
biotest@ubuntu:~/test$ bash object_exist.sh
OK on the /home/biotest directory.
Now checking on the file, sentinel.
OK on the filename
Updating Current Date...
biotest@ubuntu:~/test$ cat ~/sentinel
Sat Apr 28 00:37:31 PDT 2018

代码解释:第一次检查用了-e比较来判断用户是否有$HOME目录,如果有接下来的-e会检测sentinel文件是否存在于$HOME目录中,如果不存在,shell脚本就会提示该文件不存在,不需要更新。第二次运行时,在$HOME目录下创建了一个sentinel文件,然后重新运行脚本,由于$HOMEsentinel文件都存在,当前的上期和时间就都追加到了这个文本中。

检查文件

如果检查的对象是文件,需要用-f参数,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/bash
# Check if either a directory or file exists.
#
item_name=$HOME
echo
echo "The item being checked: $item_name"
echo
#
if [ -e $item_name ]
then # Item does exist
echo "The item, $item_name, does exist."
echo "But is it a file?"
echo
#
if [ -f $item_name ]
then # Item is a file
echo "Yes, $item_name is a file."
#
else # Item does not exist
echo "No, $item_name is not a file"
fi
else #Item doese not exist
echo "The item , $item_name, does not exist."
echo "Nothing to update"
fi

运行结果如下所示:

1
2
3
4
5
6
7
8
biotest@ubuntu:~/test$ bash test_file.sh
The item being checked: /home/biotest
The item, /home/biotest, does exist.
But is it a file?
No, /home/biotest is not a file

代码解释:首先使用了-e比较测序$HOME是否存在,如果存在,继续使用-f来测试它是不是一个文件,如果它不是文件(肯定不是文件),就会显示一条消息,它不是一个文件,如果更改一下代码,把item_name设置为$HOME/sentinel,即如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/bash
# Check if either a directory or file exists.
#
item_name=$HOME/sentinel
echo
echo "The item being checked: $item_name"
echo
#
if [ -e $item_name ]
then # Item does exist
echo "The item, $item_name, does exist."
echo "But is it a file?"
echo
#
if [ -f $item_name ]
then # Item is a file
echo "Yes, $item_name is a file."
#
else # Item does not exist
echo "No, $item_name is not a file"
fi
else #Item doese not exist
echo "The item , $item_name, does not exist."
echo "Nothing to update"
fi

运行结果如下:

1
2
3
4
5
6
7
8
biotest@ubuntu:~/test$ bash test_file2.sh
The item being checked: /home/biotest/sentinel
The item, /home/biotest/sentinel, does exist.
But is it a file?
Yes, /home/biotest/sentinel is a file.

检查文件是否可读

在试图从某个文件中读取数据之前,可以测试一下文件是否可读,参数是-r,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/bash
# testing a file is readable or not
pwfile=/etc/shadow
#
# first, test if the file exists, and is a file
if [ -f $pwfile ]
then
# Now test if you can read it
if [ -r $pwfile ]
then
tail $pwfile
else
echo "Sorry, I am unable to read the $pwfile file"
fi
else
echo "Sorry, the file $pwfile does not exist"
fi

结果如下所示:

1
2
biotest@ubuntu:~/test$ bash test_read.sh
Sorry, I am unable to read the /etc/shadow file

检查空文档

检查空文档的参数是-s,如果-s比较正确,说明文件中有数据,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash
# Testing if a file is empty
#
file_name=$HOME/sentinel
#
if [ -f $file_name ]
then
if [ -s $file_name ]
then
echo "The $file_name file exists and has data in it."
echo "Will not remove this file."
#
else
echo "The $file_name file exists, but is empty."
echo "Deleting empty file..."
rm $file_name
fi
else
echo "File, $file_name, does not exist."
fi
#

运行结果如下所示:

1
2
3
4
5
biotest@ubuntu:~/test$ ls -l $HOME/sentinel
-rw-rw-r-- 1 biotest biotest 29 Apr 28 00:37 /home/biotest/sentinel
biotest@ubuntu:~/test$ bash test_empty.sh
The /home/biotest/sentinel file exists and has data in it.
Will not remove this file.

代码解释:首先使用-f测试文件是否存在,如果存在,用-s比较来判断该文件是否为空,空文件会被删除,在运行脚本前,使用ls -l命令检查了$HOME/ssentinel文件,发现不是空的,因此脚本并不会删除它。

检查是否可写

-w比较会判断当前用户对文件是否有可写权限,下面的一段代码应时检测item_name是否存在、是否为文件,是否可写,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#!/bin/bash
# Testing if a file is writable
#
file_name=$HOME/sentinel
echo
echo "The item being checked: $file_name"
echo
#
if [ -e $file_name ]
then
echo "The item, $file_name, does exist."
echo "But is it a file?"
echo
#
if [ -f $file_name ]
then
echo "Yes, $file_name is a file."
echo "But is it writable ?"
echo
#
if [ -w $file_name ]
then
echo "Yes, it is writable."
echo "Writing current time to $file_name"
date +%H%M >> $file_name
echo
#
else
echo "The item, $file_name,is not a writable file"
echo "Unable to write to $file_name"
fi
else
echo "No, $file_name is not a file."
fi
else
echo "The item $file_name does not exist"
fi

运行结果如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
biotest@ubuntu:~/test$ bash test_write.sh
The item being checked: /home/biotest/sentinel
The item, /home/biotest/sentinel, does exit.
But is it a file?
Yes, /home/biotest/sentinel is a file.
But is it writable ?
Yes, it is writable.
Writing current time to /home/biotest/sentinel
biotest@ubuntu:~/test$ cat /home/biotest/sentinel
Sat Apr 28 00:37:31 PDT 2018
0137
0138
0141

现在使用chmod命令关闭文件sentinel的用户写入权限,再测试一下这个脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
chmod u-w $HOME/sentinel
biotest@ubuntu:~/test$ bash test_write.sh
The item being checked: /home/biotest/sentinel
The item, /home/biotest/sentinel, does exist.
But is it a file?
Yes, /home/biotest/sentinel is a file.
But is it writable ?
The item, /home/biotest/sentinel,is not a writable file
Unable to write to /home/biotest/sentinel

检查文件是否可以执行

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
# testing file execution
#
if [ -x test11.sh ]
then
echo "You can run the script: "
./test11.sh
else
echo "Sorry, you are unable to execute the script"
fi

运行结果如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
biotest@ubuntu:~/test$ bash test_ex.sh
Sorry, you are unable to execute the script
biotest@ubuntu:~/test$ ll test11.sh
-rw-rw-r-- 1 biotest biotest 195 Apr 27 23:06 test11.sh
biotest@ubuntu:~/test$ chmod u+x test11.sh
biotest@ubuntu:~/test$ ll test11.sh
-rwxrw-r-- 1 biotest biotest 195 Apr 27 23:06 test11.sh*
biotest@ubuntu:~/test$ bash test_ex.sh
You can run the script:
The test value is 5.555
./test11.sh: line 8: [: 5.555: integer expression expected

代码及结果解释:代码开头使用了-x参数来判断是滞有权限执行test11.sh脚本,如果有权限就运行这个脚本。在首次运行时,没有权限,随后使用chmod u+x test11.sh命令赋予权限,再次执行,结果显示有权限运行test11.sh这个脚本。

检查所属关系

-O(大写),测试出某个文件的属主,如下所示:

1
2
3
4
5
6
7
8
9
#!/bin/bash
# check file ownership
#
if [ -O /etc/passwd ]
then
echo "You are the owner of the /etc/passwd file"
else
echo "Sorry, you are not the owner of the /etc/passwd file"
fi

运行结果如下所示:

1
2
biotest@ubuntu:~/test$ bash owner.sh
Sorry, you are not the owner of the /etc/passwd file

代码解释:-O田头来测试运行该脚本的用户是否是/etc/passwd文件的属主,这个脚本是运行在普通有入眠账户下的,所以测试失败。

检查默认属组关系

-G比较会检查文件的默认组,如果它匹配了用户的默认组,则测试成功。由于-G比较只会检查默认组而非用户所属的所有组,因此会有一定的局限性,如下所示;

1
2
3
4
5
6
7
8
9
#!/bin/bash
# check file group test
#
if [ -G $HOME/testing ]
then
echo "Your are in the same group as the file"
else
echo "The file is not owned by your gruop"
fi

运行脚本,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
biotest@ubuntu:~$ ll testing/ #查看HOME目录下的testing目录详细信息
drwxr-xr-x 24 biotest biotest 4096 Apr 29 02:55 ../ # testing属于biotest组
biotest@ubuntu:~$ cd test
biotest@ubuntu:~/test$ bash group.sh # 运行脚本
You are in the same group as the file # testing属于当前biotest组
biotest@ubuntu:~$ cd
biotest@ubuntu:~$ sudo chgrp sharing $HOME/testing # 将testing所属的biotest组更改为sharing组
[sudo] password for biotest:
biotest@ubuntu:~$ ll testing/
drwxrwxr-x 2 biotest sharing 4096 Apr 29 00:03 ./
biotest@ubuntu:~$ cd test
biotest@ubuntu:~/test$ bash group.sh
The file is not owned by your gruop # testing就不属于了biotest组
biotest@ubuntu:~/test$ cd ..

检查文件日期

有时候需要比较两个文件的创建日期,-nt比较会判断文件A是否比另外一个文件B新,如果文件A较新,那意味着它的文件创建日期更近。-ot比较会判断一个文件A是否比另外一个文件B旧,如果文件A较旧,则是意味着它的创建日期更早。

先来查看地一下test12.sh文件与test13.sh文件,以及test11.sh和test12.sh文件,如下所示:

1
2
3
4
biotest@ubuntu:~/test$ ll test11.sh test12.sh test13.sh
-rwxrw-r-- 1 biotest biotest 196 Apr 28 20:42 test11.sh*
-rw-rw-r-- 1 biotest biotest 116 Apr 27 23:14 test12.sh
-rw-rw-r-- 1 biotest biotest 156 Apr 27 23:17 test13.sh

由上述结果可以发现,test11.sh文件创建于4.28,20:42;test12.sh文件创建于4.27,23:14,test13.sh文件创建于4.27 ,23:17,现在运行下面的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash
# testing file dates
#
if [ test12.sh -nt test13.sh ]
then
echo "The test12 file is newer than test13"
else
echo "The test13 file is newer than test12"
fi
if [ test11.sh -ot test12.sh ]
then
echo "The test11 file is older than the test12 file"
fi

运行结果如下所示:

1
2
biotest@ubuntu:~/test$ bash contrast_file.sh
The test13 file is newer than test12

代码及结果解释:test12.sh -nt test13.sh这一句用于判断test12是否比test13更新,结果是否,执行then后面的语句,即echo "The test13 file is newer than test12"。结果也显示,test13文件比test12文件更新,因为test13.sh文件创建的时间(2018-4-27-23:17)比test12.sh文件(2018-4-27-23:14)要晚。在第二个if-then语句中,test11.sh -ot test12.sh语句用于判断,test11文件是否比test12文件更老,由于test11.sh文件创建于2018-4-28-20:42;而test12.sh文件创建于2018-4-27-23:14,test11文件比test12文件创建的时候更晚,因此test11.sh -ot test12.sh这个语句不执行。

再补充一个案例,如果要比较的两个文件不存在,如果仅用-nt来判断,那么还会返回结果,如下所示:

1
2
3
4
5
6
7
8
9
#!/bin/bash
# testing file dates
#
if [ badfile1 -nt badfile2 ]
then
echo "The badfile1 file is newer than badfile2"
else
echo "The badfile2 file is newer than badfile1"
fi

运行后,结果如下所示:

1
2
biotest@ubuntu:~/test$ bash test21.sh
The badfile2 file is newer than badfile1

代码运行后,仍然有结果,这就是有问题了,因此在使用-nt时,首先需要判断文件是否存在。

复合条件测试

if-then语句中,还可以使用布尔逻辑来组合测试,有两种布尔运算符可以使用,如下所示:

1
2
3
4
5
[ condition1 ] && [ condition2 ]
# 使用的是AND布尔运行算来组合两个条件,若要执行then后面的语句,两个条件必须都满足
[ condition1 ] || [ condition2 ]
# 使用OR布尔运算符来组合两个条件,如果任意条件为TRUE,then后面的语句执行

AND运算符

1
2
3
4
5
6
7
8
9
#!/bin/bash
# testing compound comparisons
#
if [ -d $HOME ] && [ -w $HOME/testing ]
then
echo "The file exists and you can write to it"
else
echo "I cannot write to the file"
fi

运行结果如下所示:

1
2
biotest@ubuntu:~/test$ bash test22.sh
The file exists and you can write to it

if-then的高级特性

bash sehll提供了2项可在if-then语句中使用的高级特性,第一:用于数学表达式的双括号;第二:用于高级字符串处理的双方括号。

使用双括号

双括号命令可以使用户在比较过程中使用高级数学表达式。test命令只能在比较中使用简单的自述操作,而双括号命令提供了更多的数学符号,双括号的命令格式为(( expressiton ))。其中expresstion可以是任意的数学赋值或比较表达式,除了test命令使用的标准数学运算答外,下表列出了双括号命令中会用到的其他运算符,如下所示:

符号 描述
val++ 后增
val— 后减
++val 先增
—val 先减
! 逻辑求反
~ 位求反
** 幂运算
<< 左位移
>> 右位移
& 位布尔和
\ 位布尔或
&& 逻辑和
\ \ 逻辑或

案例:在if语句中使用双括号

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
# using double parenthesis
#
val1=10
#
if (( $val1 **2 > 90))
then
(( val2=$val1**2 ))
echo "The square of $val1 is $val2"
fi

运行结果如下所示:

1
2
biotest@ubuntu:~/test$ bash test23.sh
The square of 10 is 100

使用双方括号

双方括号命令提供了针对字符串比较的高级特性,双方括号命令的格式为[[ expression ]],双方括号里的expression使用了test命令中采用的标准字符串比较,但它还有另外的一个特性,即模式匹配(pattern matching)。(注:bash shel对中双方括号的支持很好,但其他的shell未必如此)。

在模式匹配中,用户可以定义一个正则表达式来匹配字符串,如下所示:

1
2
3
4
5
6
7
8
9
#!/bin/bash
# using pattern matching
#
if [[ $USER == r* ]]
then
echo "Hello $USER"
else
echo "Sorry, I do not know you"
fi

运行结果为:

1
2
biotest@ubuntu:~/test$ bash test24.sh
Sorry, I do not know you

如果将其中$USER == r*改为$USER == b*(当前Linux系统中biotest用户,b*这种格式可以查换所有以b开头的用户,运行结果如下所示:

1
2
biotest@ubuntu:~/test$ bash test24.sh
Hello biotest

case命令

如果有一种情况:用户在尝试计算一个变量的值,可以在一组可能的值中寻找特定值。在这种情况下,可以写一段长的if-then-else代码,如下所示:

常规if-then-else代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/bash
# Looking for a possible value
#
if [ $USER = "rich" ]
then
echo "Welcome $USER"
echo "Please enjoy your visit"
elif [ $USER = "barbara" ]
then
echo "Welcome $USER"
echo "Please enjoy your visit"
elif [ $USER = "testing" ]
then
echo "Special testing account"
elif [ $USER = "jessica" ]
then
echo "Do not forget to logout when you're done"
elif [ $USER = "biotest" ]
then
echo "You are currnet user"
else
echo "Sorry, your are not allowed here"
fi

运行结果如下所示:

1
2
biotest@ubuntu:~/test$ bash test25.sh
You are currnet user

如果有了case命令,则不需要写出所有的elif语句来不停地检查同一个变量的值,case命令会采用列表格式来检查单个变量的多个值。

case语句

case语句的格式如下所示:

1
2
3
4
5
case variable in
pattern1 | pattern2) command1;;
pattern3) commands2;;
*) default commands;;
esac

case命令会将指定的变量与不同械进行比较。如果变量和模式是匹配的,那么shell会执行为该模式指定的命令,可以通过竖线操作符在一行中分隔出多个模式。星号会捕获所有与已知模式不匹配的值,现在将前面的if-then-else代码转换为case形式,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/bash
# using the case command
#
case $USER in
rich | barbara)
echo "Welcome, $USER"
echo "Please enjoy your visit";;
testing)
echo "Special testing account";;
jessica)
echo "Do not forget to log off when you're done";;
biotest)
echo "You are current user";;
*)
echo "Sorry, you are not allowed here";;
esac

运行结果如下所示:

1
2
biotest@ubuntu:~/test$ bash test26.sh
You are current user