12.4 使用结构化命令之四(test命令)

test 命令

到目前为止,在if语句中看到的都是普通shell命令。那么if–then语句能否测试命令退出状态码之外的条件?
答案是:不能
我们需要借助test命令来提供if–then语句中测试不同条件的途径。如果test命令中列出的条件成立,test命令就会返回退出并返回状态码0.这样if-then语句就与其他编程语言中的if–then语句以类似的方式工作了。如果条件不成立,test命令就会退出并返回不为零的退出状态码,这使得if–then语句不会再被执行。
格式:
test condition
condition 是test命令要测试的一系列参数和值。当用在if–then语句中,test命令看起来是这样的。

if test condition
then
	commands
fi

如果不写test命令的condition部分,它会以非零的退出状态码退出,并执行else语句块。
例1:此时写了condition部分

[root@CHENDAJIE test]# cat test1 
#!/bin/bash
# Testing the test command
#
my_variable="FULL"
#
if test $my_variable
then
        echo "The $my_variable expression returns a Ture."
#
else
        echo "The $my_variable expression returns a False"
fi
[root@CHENDAJIE test]# chmod u+x test1 
[root@CHENDAJIE test]# ./test1 
The FULL expression returns a Ture.

例2:此时没写condition部分

[root@CHENDAJIE test]# cat test2
#!/bin/bash
# Testing the test command
#
my_variable=""
#
if test $my_variable
then
        echo "The $my_variable expression returns a Ture."
#
else
        echo "The $my_variable expression returns a False"
fi
[root@CHENDAJIE test]# chmod u+x test2
[root@CHENDAJIE test]# ./test2
The  expression returns a False

bash shell提供了另一种条件测试方法,无需在if–then语句中声明test命令。

if [ condition ]
then
	commands
fi

一、数值比较

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:

[root@CHENDAJIE test]# cat numberic_test.sh 
#!/bin/bash
# Using numeric test evaluations
#
value1=10
value2=11
#
if [ $valuel -gt 5 ]
then
        echo "The test value $valuel is greater than 5"
fi
#
if [ $value1 -eq $value]
then
        echo "The values are equal"
else
        echo "The values are different"
fi
#

第一个条件测试:
if [ $value1 -gt 5 ]
测试变量value1的值是否大于5.
第二个条件测试:
if [ $value1 -eq $value2 ]
测试变量value1和变量value2
执行看看:

[root@CHENDAJIE test]# ./numberic_test.sh   
The test value  is greater than 5
The values are different

注意:当涉及到浮点型的时候,数值条件测试会有一个限制。
例2:

[root@CHENDAJIE test]# cat floating_point_test.sh 
#!/bin/bash
# Using floating point numbers in test evaluations
#
value1=5.555
#
echo "The test value is $value1"
#
echo "The test value is $value1"
#
if [ $value1 -gt 5 ]
then
        echo "The test value $value1 is greater then 5"
fi
#
[root@CHENDAJIE test]# chmod u+x floating_point_test.sh ^C
[root@CHENDAJIE test]# ./floating_point_test.sh 
The test value is 5.555
The test value is 5.555
./floating_point_test.sh: 第 10 行:[: 5.555: 期待整数表达式

此例,变量value1中存储的是浮点值。接着,脚本对这个值进行了测试。但是bash中是不支持浮点型数值的。

二、字符串比较

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

1、字符串相等性

例1:字符串相等条件

[root@CHENDAJIE test]# cat test3.sh 
#!/bin/bash
# Testing string equality
testuser=chendajie
#
if [ $USER = $testuser ]
then
        echo "Welcome $testuser"
fi
#
[root@CHENDAJIE test]# ./test3.sh 
Welcome chendajie

例2:字符串不等条件

[root@CHENDAJIE test]# cat test4.sh 
#!/bin/bash
# Testing string equality
testuser=rich
#
if [ $USER != $testuser ]
then
        echo "This is not $testuser"
else
        echo "Welcome $testuser"
fi
#
[root@CHENDAJIE test]# chmod u+x test4.sh 
[root@CHENDAJIE test]# ./test4.sh 
This is not rich

在比较字符串的相等性时,比较测试会将所有的标点和大小写情况都考虑在内。

2、字符串顺序

当我们需要比较一个字符串是否比另一个字符串大时会比较麻烦。
》大于号和小于号必须转义,否则shell会把它们当作重定向符号,把字符串值当作文件名。
》大于和小于顺序和sort命令所采用的不同。
例1:举一个第一条所说的问题

[root@CHENDAJIE test]# ls
badtest.sh  floating_point_test.sh  numberic_test.sh  test1  test2  test3.sh  test4.sh
[root@CHENDAJIE test]# cat badtest.sh 
#!/bin/bash
# mis-using string comparisons
#
va11=baseball
va12=hockey
#
if [ $va11 > $va12 ]
then
        echo "$va11 is greater than $va12"
else
        echo "$va11 is less than $va12"
fi
#
[root@CHENDAJIE test]# chmod u+x badtest.sh 
[root@CHENDAJIE test]# ./badtest.sh 
baseball is greater than hockey
[root@CHENDAJIE test]# ls
badtest.sh  floating_point_test.sh  hockey  numberic_test.sh  test1  test2  test3.sh  test4.sh

注意观察,此处创建了一个新的文件hockey。而且由于重定向的顺利完成,test命令返回了退出状态码0,if语句便以为所有命令都成功执行了。
例2:将大于号转义
此时,我们先把刚刚创建的文件hockey删除,以便待会对比两次结果的不同。

[root@CHENDAJIE test]# rm -rf hockey 
[root@CHENDAJIE test]# ls
badtest.sh  floating_point_test.sh  numberic_test.sh  test1  test2  test3.sh  test4.sh

下面是代码及执行情况:

[root@CHENDAJIE test]# ls
badtest.sh  floating_point_test.sh  numberic_test.sh  test1  test2  test3.sh  test4.sh  test5.sh
[root@CHENDAJIE test]# cat test5.sh 
#!/bin/bash
# mis-using string comparisons
#
va11=baseball
va12=hockey
#
if [ $va11 \> $va12 ]
then
        echo "$va11 is greater than $va12"
else
        echo "$va11 is less $va12"
fi
#
[root@CHENDAJIE test]# chmod u+x test5.sh 
[root@CHENDAJIE test]# ./test
test1     test2     test3.sh  test4.sh  test5.sh  
[root@CHENDAJIE test]# ./test5.sh 
baseball is less hockey
[root@CHENDAJIE test]# ls
badtest.sh  floating_point_test.sh  numberic_test.sh  test1  test2  test3.sh  test4.sh  test5.sh

从上面的代码块可以看出,此时并没有创建新的文件,且输出的是baseball is less hockey。但是为什么会出现这种情况呢?命名baseball更长才对。
我们先看下一个示例。
例3:

[root@CHENDAJIE test]# ls
badtest.sh  floating_point_test.sh  numberic_test.sh  test1  test2  test3.sh  test4.sh  test5.sh  test6.sh  testing
[root@CHENDAJIE test]# cat test6.sh 
#!/bin/bash
# Testing string sort order
#
va11=Testing
va12=testing
#
if [ $va11 \> $va12 ]
then
        echo "$va11 is greater than $va12"
else
        echo "$va11 is less than $va12"
fi
#
[root@CHENDAJIE test]# chmod u+x test6.sh
[root@CHENDAJIE test]# ./test6.sh 
Testing is less than testing
[root@CHENDAJIE test]# ls
badtest.sh  floating_point_test.sh  numberic_test.sh  test1  test2  test3.sh  test4.sh  test5.sh  test6.sh  testing

在这个脚本中,我们可以发现,明明Testing和testing一样长,但是最后脚本执行的结果是Testing is less than testing。这是为什么呢?
在比较测试中,大写字母被认为是小于小写字母的。但是sort命令恰好相反。当你将同样的字符串放进文件并用sort命令排序时,小写字母会先出现。这是由于各个命令的排序方式不同造成的。
比较测试中使用的时标准的ASCII顺序,根据每个字符的ASCII数值来决定排序结果。sort命令中使用的是系统的本地化语言设置中定义的排序顺序,对于英语,本地化设置指定了在排序顺序中小写字母出现在大写字母前。

【注意】:
test命令和测试表达式使用标准的数学比较符号来表示字符串比较,而用文本代码来表示数值比较。如果你对数值使用了数学运算符号,shell会将它们当成字符串值,可能无法得到正确的结果。

3、字符串大小

-n和-z可以检查一个变量是否含有数据
例1:

[root@CHENDAJIE test]# cat test7.sh 
#!/bin/bash
# Testing string length
#
va11=testing
va12=''
#
if [ -n $va11 ]
then
        echo "The string '$va11' is not empty"
else
        echo "The sting '$va11' is empty"
fi
#
if [ -z $va12 ]
then
        echo "The string '$va12' is  empty"
else
        echo "The string '$va12' is not empty"
fi
#
if [ -z $va13 ]
then
        echo "The string '$va13' is empty"
else
        echo "The string '$va12' is not empty"
fi
#
[root@CHENDAJIE test]# chmod u+x test7.sh 
[root@CHENDAJIE test]# ./test7.sh 
The string 'testing' is not empty
The string '' is  empty
The string '' is empty

在这个例子中创建了两个字符串变量。va11变量包含了一个字符串,va12变量包含的是一个空字符串。后续的比较如下:
if [ -n $va11 ]
判断va11变量是否长度非0,而它正好长度非0,所以then部分被执行了。
if [ -z $var12 ]
判断va12变量是否长度为0,而它正好长度为0,所以then部分被执行了。
if [ -z $var13 ]
判断va13变量是否长度为0。而这个变量并未在shell脚本中定义过,所以它的字符串长度仍然为0。
【注意】:
空的和未初始化的变量会对shell脚本测试造成灾难性的影响。如果不是很确定一个变量的内容,最好在将其用于数值或字符串比较之前先通过-z或-n来测试一下变量是否含有值。
我的记法是:z—zero代表0,所以是比较是否为空字符。为空时返回0,执行then部分的语句。

三、文件比较

它允许你测试Linux文件系统上文件目录的状态。

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

1、检查目录

-d测试会检查指定目录是否存在于系统中。
例1:

[root@CHENDAJIE test]# cat test8.sh 
#!/bin/bash
# Look beefore you leap
#
jump_directory=/home/arthur
#
if [ -d $jump_directory ]
then
        echo "The $jump_directory is exits"
        cd $jump_directory
        ls
else
        echo "The $jump_directory directory does not exits"
fi
#
[root@CHENDAJIE test]# chmod u+x test8.sh 
[root@CHENDAJIE test]# ./test8.sh 
The /home/arthur directory does not exits

示例代码中使用了-d选项来检查目录arthur是否存在,如果存在就切换至该目录,不存在的就显示不存在。

2、检查对象是否存在

-e比较允许你的脚本代码在使用文件或目录前先检查它们是否存在

[root@CHENDAJIE test]# cat test9.sh 
#!/bin/bash
# Check if either a directory or file exts
#
location=$HOME
file_name="sentinel"
#
if [ -e $location ]
then     #Directory does exist
        echo "OK on the $location filename"
        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 not exist
        echo "File does not exist"
        echo "Nothing to update"
fi
#
else #File does not exits
        echo "The $location directory does not exist."
        echo "Nothing to update"
fi
#
[root@CHENDAJIE test]# ./test9.sh 
OK on the /root filename
Now checking on the file, sentinel
File does not exist
Nothing to update

切换至root目录下创建新的文件sentinel

[root@CHENDAJIE ~]# ls
anaconda-ks.cfg  file_name  initial-setup-ks.cfg  my_test
[root@CHENDAJIE ~]# touch sentinel
[root@CHENDAJIE ~]# ll -d sentinel 
-rw-r--r-- 1 root root 0 10月 16 15:23 sentine

再次执行test9.sh脚本

[root@CHENDAJIE test]# ./test9.sh 
OK on the /root filename
Now checking on the file, sentinel
File does not exist
Nothing to update

第一次检查用-e比较来判断用户是否有HOME目录。如果有,接下来的-e会检查sentinel文件是否存在于$HOME目录中,如果不存在,shell脚本就会提示该文件不存在,不需要进行更新。
为确保更新操作能够正常进行,我们创建了sentinel文件,然后重新运行这个shell脚本。这一次在进行条件测试时,HOME和sentinel文件都存在,因此当前的日期和时间就被追加到了文件中。

3、检查文件

-e比较可用于文件和目录。要确定指定对象为文件,必须用-f比较。

[root@CHENDAJIE test]# cat test10.sh 
#!/bin/bash
# Check if either a directroy 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 exits."
        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 exits
                echo "NO, $item_name is not a file."
        fi
#
else #Item does not exist
        echo "The item, $item_name, does not exist."
        echo "Nothing to update"
fi
[root@CHENDAJIE test]# chmod u+x test10.sh 
[root@CHENDAJIE test]# ./test10.sh 

The item being checked: /root

The item,/root, does exits.
But is it a file?

这一小段脚本进行了大量的检查,它首先使用-e比较测试$HOME是否存在。如果存在,继续用-f来测试它是不是一个文件。如果它不是文件,就会显示一条消息显示它不是文件。
下面这个是将刚刚test10.sh做了一点修改,将目录HOME换成文件root/sentinel

[root@CHENDAJIE test]# cat test11.sh 
#!/bin/bash
# Check if either a directroy 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 exits."
        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 exits
                echo "NO, $item_name is not a file."
        fi
#
else #Item does not exist
        echo "The item, $item_name, does not exist."
        echo "Nothing to update"
fi
[root@CHENDAJIE test]# chmod u+x test11.sh 
[root@CHENDAJIE test]# ./test11.sh 

The item being checked: /root/sentinel

The item,/root/sentinel, does exits.
But is it a file?

Yes, /root/sentinel is a file.	# 可以看到此时的显示和上面也不一样

4、检查是否可读

在尝试从文件中读取数据之前,最好先测试一下文件是否可读。可以用-r比较测试。

[root@CHENDAJIE test]# cat test12.sh 
#!/bin/bash
# Testing if you can read a file
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"
        fi
else
        echo "Sorry, the $pwfile does not exist."
fi
[root@CHENDAJIE test]# chmod u+x test12.sh 
[root@CHENDAJIE test]# ./test12.sh 
pulse:!!:18163::::::
gdm:!!:18163::::::
gnome-initial-setup:!!:18163::::::
sshd:!!:18163::::::
avahi:!!:18163::::::
postfix:!!:18163::::::
ntp:!!:18163::::::
tcpdump:!!:18163::::::
chendajie:$6$z43L0eDqqeHShai1$3mzHl3FISfHm37QIL34XrTHFoGDr6pFdHNsGdWbACU/VWjS36u.FCkbiAy/uEiIgecThHWENe7m6ngDt.XDeL1:18163:0:99999:7:::
apache:!!:18164::::::

由于此时我是root用户,所以此时我可以读取/etc/shadow文件,但是普通用户是不可读的。

5、检查空文件

用-s比较来检查文件是否为空,尤其是在不想删除非空文件的时候。要留心的是,当-s比较成功时,说明文件中有数据。

[root@CHENDAJIE test]# cat test13.sh 
#!/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
#
[root@CHENDAJIE test]# chmod u+x test13.sh 
[root@CHENDAJIE test]# ./test13.sh 
The /root/sentinel file exists, but is empty.
Deleting empty file...
[root@CHENDAJIE test]# ls /root
anaconda-ks.cfg  file_name  initial-setup-ks.cfg  my_test

-f比较测试首先测试文件是否存在。如果存在,由-s来比较该文件是否为空。空文件会被删除,由于此时我的sentinel时空文件,所以此时脚本把它删除了。

6、检查是否可写

由于上个脚本操作失误,嘿嘿。我又去root目录下新建了一个sentinal文件。并且此时我写了一丢丢数据在里面。
查看sentinel文件

[root@CHENDAJIE test]# cat /root/sentinel 
aaaa

执行脚本

[root@CHENDAJIE test]# cat test14.sh 
#!/bin/bash
# Check if a file is writable.
#
item_name=$HOME/sentinel
#
if [ -e $item_name ]
then
        echo "The item being checked: $item_name."
        echo "Yes, $item_name is a file."
        echo "But is it writable?"
        #
        if [ -w $item_name ]
        then #Item is writable
                echo "Writing current time to $item_name."
                date +%H%M >>$item_name
        #
        else #Item is not writable
                echo "Unable to write to $item_name."
        fi
else #Item is not a file
        echo "NO, $item_name is not a file."
fi
[root@CHENDAJIE test]# chmod u+x test14.sh 
[root@CHENDAJIE test]# ./test14.sh 
The item being checked: /root/sentinel.
Yes, /root/sentinel is a file.
But is it writable?
Writing current time to /root/sentinel.

此时/root/sentinel文件内容发生了改变

[root@CHENDAJIE test]# cat /root/sentinel 
aaaa
1653

下面演示一下将/root/sentinel的写权限去掉执行脚本的情况。此时不能用root用户,权限太大了额。
1、去掉文件的写权限

[root@CHENDAJIE ~]# ll -d sentinel 
-rw-r--r-- 1 root root 10 10月 16 16:53 sentinel
[root@CHENDAJIE ~]# chmod u-w sentinel 
[root@CHENDAJIE ~]# ll -d sentinel 
-r--r--r-- 1 root root 10 10月 16 16:53 sentinel

2、执行脚本:
因为我的脚本文件全部放在root目录下,此时就觉得很麻烦了,此时我将该脚本文件复制出来放在chendajie用户目录下,并且赋予执行权限。且同时要在chendajie用户的home目录下创建一个sentinel文件。唉,所以还是要好好注意这些文件的存放位置啊。**不可以大意!**复制过程就不演示了,你可以观察到下面的bash提示符发生了改变

[chendajie@CHENDAJIE ~]$ ll -d sentinel 
-r--r--r-- 1 chendajie chendajie 0 10月 16 17:06 sentinel
[chendajie@CHENDAJIE ~]$ ./test14.sh 
The item being checked: /home/chendajie/sentinel.
Yes, /home/chendajie/sentinel is a file.
But is it writable?
Unable to write to /home/chendajie/sentinel.

要注意的是这段代码块和上一段是有区别的,因为所登录的用户不一样,但是你可以使用你的普通用户来执行(指的是编辑脚本及创建文件),可以达到同样的效果。此处是我的失误,从下一篇博客开始,我会尽量使用我的普通用户来解决问题。

7、检查文件是否可以执行

-x比较是判断特定文件是否有执行权限的一个简单方法。虽然可能大多数命令用不到它,但是如果你在shell脚本中运行大量脚本,它就能发挥作用。

[root@CHENDAJIE test]# cat test15.sh 
#!/bin/bash
# Testing file execution
#
if [ -x test14.sh ]
then
        echo "You can run the scrpt: "
        ./test14.sh
else
        echo "Sorry, you are unable to execute the script."
fi
#
[root@CHENDAJIE test]# chmod u+x test15.sh 
[root@CHENDAJIE test]# ./test15.sh 
You can run the scrpt: 
The item being checked: /root/sentinel.
Yes, /root/sentinel is a file.
But is it writable?
Writing current time to /root/sentinel

这段示例shell脚本用-x比较来测试是否具有权限执行test14.sh脚本。如果有权限,它会执行这个脚本。如果你将test.14权限更改,-x就会比较失败了,因为你已经没有了test14.sh的执行权限了。

8、检查所属关系

-O可以测试出你是否是文件的属主。

[root@CHENDAJIE test]# cat test16.sh 
#!/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
#
[root@CHENDAJIE test]# chmod u+x test16.sh 
[root@CHENDAJIE test]# ./test16.sh 
You are the owner of the /etc/passwd file

由于此时我的登录用户是root用户,所以脚本执行成功了。

9、检查默认属主关系

-G比较会检查文件的默认组,如果它匹配了用户的默认组,则测试成功。-G比较只会检查默认组而非用户所属的所有组。

[root@CHENDAJIE test]# cat test17.sh 
#!/bin/bash
# Check file group test
#
if [ -G $HOME/testing ]
then
        echo "You are in the same group as the file."
else
        echo "The file is not owned by your group."
fi
#
[root@CHENDAJIE test]# chmod u+x test17.sh 
[root@CHENDAJIE test]# ./test17.sh 
The file is not owned by your group.

10、检查文件日期

最后一组方法用来对两个文件的创建日期进行比较。这在编写软件安装脚本时非常有用。有时候,你不会想要安装一个比系统上已有文件还要旧的文件。
-nt比较会判定一个文件是否比另一个文件新。如果文件较新,那意味着它的文件创建日期更近。-ot比较会判定一个文件是否比另一个文件旧,如果文件较旧,意味着它的创建日期更早。

[root@CHENDAJIE test]# cat test18.sh 
#!/bin/bash
# Testing file dates
#
if [ test17.sh -nt test16.sh ]
then
        echo "The test17 file is newer than test16."
else
        echo "The test17 file is older than test16."
fi
if [ test16.sh -ot test17.sh ]
then
        echo "The test16 file is older than the test17 file."
fi
#
[root@CHENDAJIE test]# chmod u+x test18.sh 
[root@CHENDAJIE test]# ./test18.sh 
The test17 file is newer than test16.
The test16 file is older than the test17 file.

用于比较文件路径是相对你运行该脚本的目录而言的。如果你要检查的文件已经移走,就会出现问题。另一个问题是,这些比较都不会先检查文件是否存在。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值