test 是 Shell 内置命令,用来检测某个条件是否成立。test 通常和 if 语句一起使用,并且大部分 if 语句都依赖 test。

test 命令有很多选项,可以进行数值、字符串和文件三个方面的检测。

Shell test 命令的用法为:

test expression

当 test 判断 expression 成立时,退出状态为 0,否则为非 0 值。

test 命令也可以简写为[],它的用法为:

[ expression ]

注意[]expression之间的空格,这两个空格是必须的,否则会导致语法错误。[]的写法更加简洁,比 test 使用频率高。

test 和 [] 是等价的,后续我们会交替使用 test 和 [],以让读者尽快熟悉。

在《Shell if else》中,我们使用 (()) 进行数值比较,这节我们就来看一下如何使用 test 命令进行数值比较。

  1. #!/bin/bash
  2. read age
  3. if test $age -le 2; then
  4. echo "婴儿"
  5. elif test $age -ge 3 && test $age -le 8; then
  6. echo "幼儿"
  7. elif [ $age -ge 9 ] && [ $age -le 17 ]; then
  8. echo "少年"
  9. elif [ $age -ge 18 ] && [ $age -le 25 ]; then
  10. echo "成年"
  11. elif test $age -ge 26 && test $age -le 40; then
  12. echo "青年"
  13. elif test $age -ge 41 && [ $age -le 60 ]; then
  14. echo "中年"
  15. else
  16. echo "老年"
  17. fi

其中,-le选项表示小于等于,-ge选项表示大于等于,&&是逻辑与运算符。

学习 test 命令,重点是学习它的各种选项,下面我们就逐一讲解。

1) 与文件检测相关的 test 选项

表1:test 文件检测相关选项列表
文件类型判断
选 项 作 用
-b filename 判断文件是否存在,并且是否为块设备文件。
-c filename 判断文件是否存在,并且是否为字符设备文件。
-d filename 判断文件是否存在,并且是否为目录文件。
-e filename 判断文件是否存在。
-f filename 判断文件是否存在,井且是否为普通文件。
-L filename 判断文件是否存在,并且是否为符号链接文件。
-p filename 判断文件是否存在,并且是否为管道文件。
-s filename 判断文件是否存在,并且是否为非空。
-S filename 判断该文件是否存在,并且是否为套接字文件。
文件权限判断
选 项 作 用
-r filename 判断文件是否存在,并且是否拥有读权限。
-w filename 判断文件是否存在,并且是否拥有写权限。
-x filename 判断文件是否存在,并且是否拥有执行权限。
-u filename 判断文件是否存在,并且是否拥有 SUID 权限。
-g filename 判断文件是否存在,并且是否拥有 SGID 权限。
-k filename 判断该文件是否存在,并且是否拥有 SBIT 权限。
文件比较
选 项 作 用
filename1 -nt filename2 判断 filename1 的修改时间是否比 filename2 的新。
filename -ot filename2 判断 filename1 的修改时间是否比 filename2 的旧。
filename1 -ef filename2 判断 filename1 是否和 filename2 的 inode 号一致,可以理解为两个文件是否为同一个文件。这个判断用于判断硬链接是很好的方法

Shell test 文件检测举例:

  1. #!/bin/bash
  2. read filename
  3. read url
  4. if test -w $filename && test -n $url
  5. then
  6. echo $url > $filename
  7. echo "写入成功"
  8. else
  9. echo "写入失败"
  10. fi

在 Shell 脚本文件所在的目录新建一个文本文件并命名为 urls.txt,然后运行 Shell 脚本,运行结果为:
urls.txt↙
http://c.biancheng.net/shell/↙
写入成功

2) 与数值比较相关的 test 选​项

表2:test 数值比较相关选项列表
选 项 作 用
num1 -eq num2 判断 num1 是否和 num2 相等。
num1 -ne num2 判断 num1 是否和 num2 不相等。
num1 -gt num2 判断 num1 是否大于 num2 。
num1 -lt num2 判断 num1 是否小于 num2。
num1 -ge num2 判断 num1 是否大于等于 num2。
num1 -le num2 判断 num1 是否小于等于 num2。

注意,test 只能用来比较整数,小数相关的比较还得依赖 bc 命令

Shell test 数值比较举例:

  1. #!/bin/bash
  2. read a b
  3. if test $a -eq $b
  4. then
  5. echo "两个数相等"
  6. else
  7. echo "两个数不相等"
  8. fi

运行结果1:
10 10
两个数相等

运行结果2:
10 20
两个数不相等

3) 与字符串判断相关的 test 选项

表3:test 字符串判断相关选项列表
选 项 作 用
-z str 判断字符串 str 是否为空。
-n str 判断宇符串 str 是否为非空。
str1 = str2
str1 == str2
===是等价的,都用来判断 str1 是否和 str2 相等。
str1 != str2 判断 str1 是否和 str2 不相等。
str1 \> str2 判断 str1 是否大于 str2。\>>的转义字符,这样写是为了防止>被误认为成重定向运算符。
str1 \< str2 判断 str1 是否小于 str2。同样,\<也是转义字符。

C语言C++PythonJava 等编程经验的读者请注意,==、>、< 在大部分编程语言中都用来比较数字,而在 Shell 中,它们只能用来比较字符串,不能比较数字,这是非常奇葩的,大家要习惯。

其次,不管是比较数字还是字符串,Shell 都不支持 >= 和 <= 运算符,切记。

Shell test 字符串比较举例:

  1. #!/bin/bash
  2. read str1
  3. read str2
  4. #检测字符串是否为空
  5. if [ -z "$str1" ] || [ -z "$str2" ]
  6. then
  7. echo "字符串不能为空"
  8. exit 0
  9. fi
  10. #比较字符串
  11. if [ $str1 = $str2 ]
  12. then
  13. echo "两个字符串相等"
  14. else
  15. echo "两个字符串不相等"
  16. fi

运行结果:
http://c.biancheng.net/
http://c.biancheng.net/shell/
两个字符串不相等

细心的读者可能已经注意到,变量 $str1 和 $str2 都被双引号包围起来,这样做是为了防止 $str1 或者 $str2 是空字符串时出现错误,本文的后续部分将为你分析具体原因。

4) 与逻辑运算相关的 test 选项

表4:test 逻辑运算相关选项列表
选 项 作 用
expression1 -a expression 逻辑与,表达式 expression1 和 expression2 都成立,最终的结果才是成立的。
expression1 -o expression2 逻辑或,表达式 expression1 和 expression2 有一个成立,最终的结果就成立。
!expression 逻辑非,对 expression 进行取反。

改写上面的代码,使用逻辑运算选项:

  1. #!/bin/bash
  2. read str1
  3. read str2
  4. #检测字符串是否为空
  5. if [ -z "$str1" -o -z "$str2" ] #使用 -o 选项取代之前的 ||
  6. then
  7. echo "字符串不能为空"
  8. exit 0
  9. fi
  10. #比较字符串
  11. if [ $str1 = $str2 ]
  12. then
  13. echo "两个字符串相等"
  14. else
  15. echo "两个字符串不相等"
  16. fi

前面的代码我们使用两个[]命令,并使用||运算符将它们连接起来,这里我们改成-o选项,只使用一个[]命令就可以了。

在 test 中使用变量建议用双引号包围起来

test 和 [] 都是命令,一个命令本质上对应一个程序或者一个函数。即使是一个程序,它也有入口函数,例如C语言程序的入口函数是 main(),运行C语言程序就从 main() 函数开始,所以也可以将一个程序等效为一个函数,这样我们就不用再区分函数和程序了,直接将一个命令和一个函数对应起来即可。

有了以上认知,就很容易看透命令的本质了:使用一个命令其实就是调用一个函数,命令后面附带的选项和参数最终都会作为实参传递给函数。

假设 test 命令对应的函数是 func(),使用test -z $str1命令时,会先将变量 $str1 替换成字符串:

  • 如果 $str1 是一个正常的字符串,比如 abc123,那么替换后的效果就是test -z abc123,调用 func() 函数的形式就是func("-z abc123")。test 命令后面附带的所有选项和参数会被看成一个整体,并作为实参传递进函数。
  • 如果 $str1 是一个空字符串,那么替换后的效果就是test -z,调用 func() 函数的形式就是func("-z "),这就比较奇怪了,因为-z选项没有和参数成对出现,func() 在分析时就会出错。

如果我们给 $str1 变量加上双引号,当 $str1 是空字符串时,test -z "$str1"就会被替换为test -z "",调用 func() 函数的形式就是func("-z \"\""),很显然,-z选项后面跟的是一个空字符串(\"表示转义字符),这样 func() 在分析时就不会出错了。

所以,当你在 test 命令中使用变量时,我强烈建议将变量用双引号""包围起来,这样能避免变量为空值时导致的很多奇葩问题。

总结

test 命令比较奇葩,>、<、== 只能用来比较字符串,不能用来比较数字,比较数字需要使用 -eq、-gt 等选项;不管是比较字符串还是数字,test 都不支持 >= 和 <=。有经验的程序员需要慢慢习惯 test 命令的这些奇葩用法。

对于整型数字的比较,我建议大家使用 (()),这在《Shell if else》中已经进行了演示。(()) 支持各种运算符,写法也符合数学规则,用起来更加方便,何乐而不为呢?

几乎完全兼容 test ,并且比 test 更加强大,比 test 更加灵活的是[[ ]][[ ]]不是命令,而是 Shell 关键字,下节《Shell [[]]》我们将会讲解

Shell test命令(Shell [])详解,附带所有选项及说明

Shell test命令(Shell [])详解,附带所有选项及说明的更多相关文章

  1. Linux Shell脚本编程--nc命令使用详解

    linux nc命令使用详解     功能说明:功能强大的网络工具 语 法:nc [-hlnruz][-g<网关...>][-G<指向器数目>][-i<延迟秒数>] ...

  2. Linux Shell脚本入门--wget 命令用法详解

    Linux Shell脚本入门--wget 命令用法详解 wget是在Linux下开发的开放源代码的软件,作者是Hrvoje Niksic,后来被移植到包括Windows在内的各个平台上.它有以下功能 ...

  3. (转)shell中test命令方法详解

    test命令用法.功能:检查文件和比较值 shell中test命令方法详解 原文:https://www.cnblogs.com/guanyf/p/7553940.html 1)判断表达式 if te ...

  4. shell编程系列23--shell操作数据库实战之mysql命令参数详解

    shell编程系列23--shell操作数据库实战之mysql命令参数详解 mysql命令参数详解 -u 用户名 -p 用户密码 -h 服务器ip地址 -D 连接的数据库 -N 不输出列信息 -B 使 ...

  5. Linux Shell数组常用操作详解

    Linux Shell数组常用操作详解 1数组定义: declare -a 数组名 数组名=(元素1 元素2 元素3 ) declare -a array array=( ) 数组用小括号括起,数组元 ...

  6. Ubuntu kill命令用法详解

    转自:Ubuntu kill命令用法详解 1. kill   作用:根据进程号杀死进程   用法: kill [信号代码] 进程ID   root@fcola:/# ps -ef | grep sen ...

  7. Mysql数据库导入命令Source详解

    Mysql数据库导入命令Source详解 几个常用用例: 1.导出整个数据库 mysqldump -u 用户名 -p 数据库名 > 导出的文件名 mysqldump -u root -p dat ...

  8. linux nc命令使用详解(转)

    linux nc命令使用详解 功能说明:功能强大的网络工具 语 法:nc [-hlnruz][-g<网关...>][-G<指向器数目>][-i<延迟秒数>][-o& ...

  9. Make命令完全详解教程

    Make命令完全详解教程 无论是在Linux还是在Unix环境中,make都是一个非常重要的编译命令.不管是自己进行项目开发还是安装应用软件,我们都经常要用到make或make install.利用m ...

  10. Scrapy框架的命令行详解【转】

    Scrapy框架的命令行详解 请给作者点赞 --> 原文链接 这篇文章主要是对的scrapy命令行使用的一个介绍 创建爬虫项目 scrapy startproject 项目名例子如下: loca ...

随机推荐

  1. PAT 乙级练习题1001 害死人不偿命的(3n+1)猜想 (15)

    1001. 害死人不偿命的(3n+1)猜想 (15) 卡拉兹(Callatz)猜想: 对任何一个自然数n,如果它是偶数,那么把它砍掉一半:如果它是奇数,那么把(3n+1)砍掉一半.这样一直反复砍下去, ...

  2. java nio socket实例

    Server端代码: public class NioServer { //通道管理器 private Selector selector; //获取一个ServerSocket通道,并初始化通道 p ...

  3. JS window对象 Location对象 location用于获取或设置窗体的URL,并且可以用于解析URL。 语法: location.[属性|方法]

    Location对象 location用于获取或设置窗体的URL,并且可以用于解析URL. 语法: location.[属性|方法] location对象属性图示: location 对象属性: lo ...

  4. vue+vue-cli2+webpack配置资源cdn

    vue-cli2+webpack构建的vue项目如何让图片和js等静态资源走cdn,哪里可以配置呢?下面我详细介绍 1.config/index.js中可以看到 module.exports = { ...

  5. 了解跨站请求伪造CSRF

    参考以下两篇文章: https://www.cnblogs.com/Erik_Xu/p/5481441.html https://www.cnblogs.com/4littleProgrammer/p ...

  6. spring framework三个版本的下载包区别

    docs:该文件夹下包含Spring的相关文档.开发指南及API参考文档:dist:该文件夹下包含Spring jar包.文档.项目等内容:schema:里面包含了Spring4所用到的xsd文件:

  7. Angular.js分页代码

                $scope.reloadList=function(){                             $scope.findPage( $scope.pagina ...

  8. demo BaseDao随笔,hibernate框架

    /** * 增加entity * * @param Object对象 * @throws Exception */ public <T> void save(T ob) throws Ex ...

  9. idea创建Maven项目后启动报404

    这块的配置是

  10. PHP ftp_login() 函数

    定义和用法 ftp_login() 函数登录 FTP 服务器. 如果成功,该函数返回 TRUE.如果失败,则返回 FALSE 和一个警告. 语法 ftp_login(ftp_connection,us ...