为了能够正确处理Shell程序运行过程中遇到的各种情况,Linux Shell提供了一组测试运算符。通过 这些运算符,Shell程序能够判断某种或者几个条件是否成立。条件测试在各种流程控制语句,例如判断 语句和循环语句中发挥了重要的作用,所以,了解和掌握这些条件测试是非常重要的。
在shell程序中,用户可以使用测试语句来测试指定的条件表达式的条件的真或假。当指定的条件为真
时,整个条件测试的返回值为0
;反之,如果指定的条件为假
,则条件测试语句的返回值为非0
值。 (shell中真0,假非0
)
条件测试语法 | 说明 |
---|---|
test <测试表达式> | test和测试表达式之间至少有一个空格 |
[ <测试表达式> ] | 和test用法一致,中括号[]的边界和测试表达式之间至少一个空格 |
[[ <测试表达式> ]] | 比test和[]更新的语法,[[]]边界和测试表达式之间至少一个空格,[[]]中可使用通配符等 进行模式匹配 |
((<测试表达式>)) | 一般用于if语句,双小括号两边不需要有空格,测试对象只能是整数 |
条件测试判断可分三类:
文件比较
字符串比较
数值比较
[root@node-254 yurq]# touch file
[root@node-254 yurq]# test -n file
[root@node-254 yurq]# echo $?
0
[root@node-254 yurq]# [ -a file ]
[root@node-254 yurq]# echo $?
0
[root@node-254 yurq]# [ -a file1 ]
[root@node-254 yurq]# echo $?
1
[root@node-254 yurq]# [[ -a file ]]
[root@node-254 yurq]# echo $?
0
[root@node-254 yurq]# [[ -a file1 ]]
[root@node-254 yurq]# echo $?
1
操作符 | 说明 |
---|---|
-e | 检测文件是否存在 |
-a | 检测文件是否存在,等价于 -e。不推荐使用,已被弃用。 |
-f | 文件是常规文件(regular file),而非目录或 设备文件 |
-s | 文件大小不为0 |
-d | 文件是一个目录 |
-b | 文件是一个块设备 |
-c | 文件是一个 字符设备 |
-p | 文件是一个 管道设备,FIFO(pipe) |
-h | 文件是一个符号链接 |
-L | 文件是一个符号链接 |
-S | 文件是一个 套接字 |
-t | 文件(文件描述符)与终端设备关联。该选项通常被用于 测试脚本中的 stdin [ -t 0 ] 或 stdout [ -t 1 ] 是否为终端设备 |
-r | 该文件对执行测试的用户可读 |
-w | 该文件对执行测试的用户可写 |
-x | 该文件可被执行测试的用户所执行 |
-g | 文件或目录设置了 set-group-id sgid 标志。 |
-u | 文件设置了 set-user-id suid 标志。 |
-k | 设置了粘滞位(sticky bit)标志粘滞位是一种特殊的文件权限。 |
-O | 执行用户是文件的拥有者 |
-G | 文件的组与执行用户的组相同 |
-N | 文件在在上次访问后被修改过了 |
[root@node-254 yurq]# test -c /dev/zero
[root@node-254 yurq]# echo $?
0
[root@node-254 yurq]# test -b /dev/sda
[root@node-254 yurq]# echo $?
0
[root@node-254 yurq]# test -h /dev/stdout
[root@node-254 yurq]# echo $?
0
[root@node-254 yurq]# test -L /dev/stdout
[root@node-254 yurq]# echo $?
0
-e:判断文件/目录是否存在,存在为0(真),否则为非0(假)
-f:判断该【文件名】是否为文件
-d:判断该【文件名】是否为目录
-b:判断该【文件名】是否为block device
-c:判断该【文件名】是否为character device
-S:判断该【文件名】是否为socket device
-P:判断该【文件名】是否为FIFO(pipe)文件
-L:判断该【文件名】是否为连结档
-r:判断该【文件名】是否具有可读属性
-w:判断该【文件名】是否具有可写属性
-x:判断该【文件名】是否具有可执行属性
-u:判断该【文件名】是否具有suid属性
-g:判断该【文件名】是否具有sgid属性
-k:判断该【文件名】是否具有Sticky bit属性
-s:判断该【文件名】是否为非空白文件
操作符 | 说明 |
---|---|
-nt | (newer than)判断file1 是否比 file2 新 |
-ot | (older than)判断file1 是否比 file2 旧 |
-ef | 判断file1和file2是否为同一文件,主要用于判断文件是否指向同一个inode |
[root@node-254 yurq]# touch file1
[root@node-254 yurq]# test file -ef file21
[root@node-254 yurq]# echo $?
1
[root@node-254 yurq]# test file -nt file1
[root@node-254 yurq]# echo $?
1
[root@node-254 yurq]# test file -ot file1
[root@node-254 yurq]# echo $?
0
[root@node-254 yurq]# ln file file2
[root@node-254 yurq]# test file -ef file2
[root@node-254 yurq]# echo $?
0
字符串比较符
操作符 | 说明 |
---|---|
str1 = str2 | 检查是否相同,可使用== 代替= |
str1 != str2 | 是否不同 |
str1 > str2 | str1是否比str2大 |
str1 < str2 | str1是否比str2小 |
-n str1 | 检查str1长度是否非0 |
-z str2 | 检查str2长度是否为0,-z 可以理解为zero |
注意事项:
- 字符串比较用“符号”比较符,而数值用“字母”比较符
- 字符串比较符两边都必须有空格
- 字符串比较按照ASCII码顺序比较,而sort则根据本地化语言中设置中定义的顺序排序
- 字符串大小比较要使用转义符
\
防止重定向,例如[ $str1\>
$str2 ]
[root@node-254 yurq]# a='abc'
[root@node-254 yurq]# b='def'
[root@node-254 yurq]# test $a = $b
[root@node-254 yurq]# echo $?
1
[root@node-254 yurq]# test $a != $b
[root@node-254 yurq]# echo $?
0
[root@node-254 yurq]# test $a > $b
[root@node-254 yurq]# echo $?
0
[root@node-254 yurq]# test $a < $b
[root@node-254 yurq]# echo $?
0
[root@node-254 yurq]# test $a > $b
[root@node-254 yurq]# echo $?
0
[root@node-254 yurq]# test $a \> $b
[root@node-254 yurq]# echo $?
1
[root@node-254 yurq]# test -n $a
[root@node-254 yurq]# echo $?
0
[root@node-254 yurq]# test -z $a
[root@node-254 yurq]# echo $?
1
整数比较符
操作符 | 说明 |
---|---|
n1 -eq n2 | 检查n1是否与n2相等 |
n1 -ge n2 | 大于或等于 |
n1 -gt n2 | 大于 |
n1 -le n2 | 小于等于 |
n1 -lt n2 | 小于 |
n1 -ne n2 | 不等于 |
[root@node-254 yurq]# a=1
[root@node-254 yurq]# b=2
[root@node-254 yurq]# test $a -eq $b
[root@node-254 yurq]# echo $?
1
[root@node-254 yurq]# test $a -ge $b
[root@node-254 yurq]# echo $?
1
[root@node-254 yurq]# test $a -gt $b
[root@node-254 yurq]# echo $?
1
[root@node-254 yurq]# test $a -le $b
[root@node-254 yurq]# echo $?
0
[root@node-254 yurq]# test $a -lt $b
[root@node-254 yurq]# echo $?
0
[root@node-254 yurq]# test $a -ne $b
[root@node-254 yurq]# echo $?
0
注意:
浮点数不能使用数值比较,可借助bc
[root@node-254 yurq]# bc
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
a=3.1415
b=2
a>b
1
a<b
0
a!=b
1
执行效果
条件1(命令1) && 条件2(命令2)
条件 | 执行结果 |
---|---|
条件1为假 | 条件2不执行 |
条件1为真 | 条件2执行 |
[root@node-254 yurq]# a=1;b=2
[root@node-254 yurq]# test $a -gt $b && echo true
[root@node-254 yurq]# test $a -lt $b && echo true
true
条件1(命令1) || 条件2(命令2)
条件 | 执行结果 |
---|---|
条件1为真 | 条件2不执行 |
条件1为假 | 条件2执行 |
[root@node-254 yurq]# a=1;b=2
[root@node-254 yurq]# test $a -lt $b || echo true
[root@node-254 yurq]# test $a -gt $b || echo true
true
取反
[root@node-254 yurq]# [ ! -d file ]
[root@node-254 yurq]# echo $?
0
[root@node-254 yurq]# [ ! -f file ]
[root@node-254 yurq]# echo $?
1
前面提到执行效果,而在条件测试中,执行效果如下
对比
在[]中使用的逻辑操作符 | 在test、[[]]和(())中的逻辑操作符 | 说明 |
---|---|---|
-a | && | -a代表and,两端都为真,则为真 |
-o | 竖线竖线 | -o代表or,两端有一个为真,则为真 |
! | ! | not,结果取反为真,则为真 |
[root@node-254 yurq]# [ $a -gt $b -a $b -lt $a ] && echo true || echo false
false
[root@node-254 yurq]# [ $a -lt $b -a $b -gt $a ] && echo true || echo false
true
[root@node-254 yurq]# [ $a -lt $b -o $b -gt $a ] && echo true || echo false
true
[root@node-254 yurq]# [ $a -gt $b -o $b -lt $a ] && echo true || echo false
false
[root@node-254 yurq]# [ ! $a -gt $b ] &&echo true || echo false
true
[root@node-254 yurq]# [ ! $a -lt $b ] &&echo true || echo false
false
[root@node-254 yurq]# test $a -gt $b && test $b -lt $a && echo true || echo false
false
[root@node-254 yurq]# test $a -lt $b && test $b -gt $a && echo true || echo false
true
[[ ]]、(( ))参考test
符号 | 说明 |
---|---|
竖线 | 管道符号,他的功能是把第一个命令1执行的结果,作为命令2的输入传给命令2 |
&& | 与操作符,&&左边的命令(命令1)返回真(即返回0,成功被执行)后,&&右边的命令(命令2)才能够被执行;换句话说,“条件1为真,条件2执行,条件1为假,条件2不执行”。 |
竖线竖线 | 如果或运算符左边的命令(command1)未执行成功,那么就执行或运算符右边的命令(command2) |
() | () 表示在当前 shell 中将多个命令作为一个整体执行 |
| 竖线
[root@node-254 yurq]# ll
total 0
-rw-r--r-- 1 root root 0 Jun 13 16:30 2
-rw-r--r-- 1 root root 0 Jun 13 16:00 def
-rw-r--r-- 2 root root 0 Jun 13 11:55 file
-rw-r--r-- 1 root root 0 Jun 13 14:38 file1
-rw-r--r-- 2 root root 0 Jun 13 11:55 file2
[root@node-254 yurq]# man ls
[root@node-254 yurq]# ls | sort -nr
2
file2
file1
file
def
格式
command1 && command2 && command3 ...
[root@node-254 yurq]# test $a -lt $b&&test $b -gt $a && echo true
true
格式
command1 || command2 || command3 ...
||则与&&相反。如果||左边的命令(command1)未执行成功,那么就执行||右边的命令(command2)
[root@node-254 yurq]# echo $a,$b
1,2
[root@node-254 yurq]# test $a -lt $b || test $b -gt $a || echo true
()表示在当前 shell 中将多个命令作为一个整体执行
格式:
(command1;command2;command3....) #多个命令之间用;分隔
使用 () 括起来的命令在执行前面都不会切换当前工作目录
,也就是说命令组合都是在当前工作目录下被执行的,尽管命令中有切换目录的命令。[root@node-254 yurq]# (echo $a;echo $b)
1
2
[root@node-254 yurq]# (pwd;cd /home;pwd)
/home/yurq
/home
[root@node-254 yurq]#
[root@node-254 yurq]# if [ -n pwd ];then (pwd); fi #这种情况加不加()效果一样
/home/yurq
echo (pwd)语法错误,笔者这样理解,(pwd)本身把执行结果输出到标准输出,echo同样把结果输出到标准输出,所以重复,导致报错。
大括号拓展。(通配(globbing))将对大括号中的文件名做扩展。在大括号中,不允许有空白,除非这个空白被引用或转义。
第一种:对大括号中的以逗号分割的文件列表进行拓展。如 touch {a,b}.txt 结果为a.txt b.txt。
第二种:对大括号中以点点(…)分割的顺序文件列表起拓展作用,如:touch {a…d}.txt 结果为a.txt b.txt c.txt d.txt
代码块,又被称为内部组,这个结构事实上创建了一个匿名函数 。与小括号中的命令不同,大括号内的命令不会新开一个子shell运行
,即脚本余下部分仍可使用括号内变量。括号内的命令间用分号隔开,最后一个也必须有分号。{}的第一个命令和左括号之间必须要有一个空格。
语法
{ command1;command2;command3… } #注意:在使用{}时,{}与命令之间必须使用一个空格
[root@node-254 yurq]# ll
total 0
[root@node-254 yurq]# touch {1..3}.txt
[root@node-254 yurq]# ll
total 0
-rw-r--r-- 1 root root 0 Jun 14 08:40 1.txt
-rw-r--r-- 1 root root 0 Jun 14 08:40 2.txt
-rw-r--r-- 1 root root 0 Jun 14 08:40 3.txt
[root@node-254 yurq]# a=1;echo $a;{ a=2; };echo $a
1
2
变量 | 说明 |
---|---|
$# | 表示执行脚本传入参数的个数 |
$* | 表示执行脚本传入参数的列表(不包括$0),看做一个整体 |
$$ | 表示进程的id;Shell本身的PID(ProcessID,即脚本运行的当前 进程ID号) |
$! | 获得之前(上一个)后台进程 ID(后台运行的最后一个进程的 进程ID号) |
$@ | 表示获取执行脚本传入的所有参数 |
$0 | 表示执行的脚本名称 |
$1 | 表示第一个参数 |
$2 | 表示第二个参数 |
$? | 表示脚本执行的状态,0表示正常,其他表示错误 |
$@
和$*
都表示命令行所有的参数(不包含$0
),但是$*
将命令行所有的参数看成一个整体,而$@
则区分各个参数[root@node-254 yurq]# cat test.sh
#!/bin/sh
MY_SHELL_PATH=`dirname $0`
echo "print shell script location:"
echo ${MY_SHELL_PATH}
echo "===================================="
echo "enter shell script location:${MY_SHELL_PATH}"
cd `dirname $0`
echo "list current directory content:"
ls -lh
echo "===================================="
echo "shell script name=${0}"
echo "===================================="
echo "first args=${1}"
echo "===================================="
echo "second args=${2}"
echo "===================================="
echo "arguments number=$#"
echo "===================================="
echo "arguments list content(@)=$@"
echo "===================================="
echo "arguments list content(*)=$*"
echo "===================================="
echo "the process id is " "$$"
echo "===================================="
echo "the shell execute return value is " "$?"
echo "===================================="
echo "the all paramters(@):"
for i in "$@"
do
echo $i #循环$#次
done
echo "===================================="
echo "the all paramters(*):"
for i in "$*"
do
echo $i
done
echo "===================================="
[root@node-254 yurq]# ./test.sh parameter1 parameter2
print shell script location:
.
====================================
enter shell script location:.
list current directory content:
total 4.0K
-rw-r--r-- 1 root root 0 Jun 14 08:40 1.txt
-rw-r--r-- 1 root root 0 Jun 14 08:40 2.txt
-rw-r--r-- 1 root root 0 Jun 14 08:40 3.txt
-rwxr--r-- 1 root root 1.2K Jun 14 08:52 test.sh
====================================
shell script name=./test.sh
====================================
first args=parameter1
====================================
second args=parameter2
====================================
arguments number=2
====================================
arguments list content(@)=parameter1 parameter2
====================================
arguments list content(*)=parameter1 parameter2
====================================
the process id is 16696
====================================
the shell execute return value is 0
====================================
the all paramters(@):
parameter1
parameter2
====================================
the all paramters(*):
parameter1 parameter2
====================================
[root@node-254 yurq]# sleep 10 &
[1] 16780
[root@node-254 yurq]# echo $!
16780
当shell执行一个程序时,会要求UNIX内核启动一个新的进程,以便在该进程里执行所指定的程序。
当系统只有一个shell时,退回到/bin/sh 的机制非常方便,但现行的UNIX系统都会拥有好几个shell,此时就需要一种方式,指定由那个shell来执行所指定的shell脚本。
由此引入了#!
这两个字符。
当一个文件种的开头是#!
时,内核会扫描改行的其余部分,看是否存在可用开执行程序的解释器的完整路径(注:中间出现任何空白符号都会略过)