本文主要对Shell中的运算符进行简单总结,另外本文所使用的Linux环境为CentOS Linux release 8.1.1911
,所使用的Shell为bash 4.4.19(1)-release
。
一、算术运算符
运算符 | 说明 | 示例 |
---|---|---|
+ | 单目正号 | ((+3)) 结果为3 |
- | 单目负号 | ((-3)) 结果为-3 |
++ | 自增(变量前,先运算后取值;变量后,先取值后运算) | b=2 时((a=++b)) 后a为3,b为3;((a=b++)) 后a为2,b为3 |
– | 自减(变量前,先运算后取值;变量后,先取值后运算) | b=2 时((a=--b)) 后a为1,b为1;((a=b--)) 后a为2,b为1 |
+ | 加 | expr 3 + 2 结果为5 |
- | 减 | expr 3 - 2 结果为1 |
* | 乘 | expr 3 \* 2 结果为6 |
/ | 除 | expr 3 / 2 结果为1 |
% | 取模 | expr 3 % 2 结果为1 |
** | 幂运算 | ((3**2)) 结果为9 |
expr
可用于整数运算,也可以处理字符串,使用expr
进行运算时,表达式和运算符之间必须加空格,乘号*
和小括号()
前要加\
转义(小括号是左右括号前都要加\
转义)。
(( expression ))
会对算术表达式求值,如果表达式的值不是0,则返回状态是0,否则返回状态是1,这和let "expression
"等价。
在(( ))
前面加上$
符号可以获取(( ))
命令的执行结果,即整个表达式的值。
(( ))
只能进行整数运算,不能对浮点数或字符串进行运算。
示例如下:
#!/bin/bash
# 单目正负号
echo "单目正负号"
a=3
echo $((+a)) $((-a))
# 自增自减运算
echo "自增自减运算"
b=2
echo "b:${b}"
((a=++b))
echo "a=++b : a:${a},b:${b}"
b=2
((a=b++))
echo "a=b++ : a:${a},b:${b}"
b=2
((a=--b))
echo "a=--b : a:${a},b:${b}"
b=2
((a=b--))
echo "a=b-- : a:${a},b:${b}"
# 加减乘除、取模、幂运算
echo "加减乘除、取模、幂运算"
a=3
b=2
echo "a:${a},b:${b}"
echo "a + b : `expr ${a} + ${b}`"
echo "a - b : `expr ${a} - ${b}`"
echo "a * b : `expr ${a} \* ${b}`"
echo "a / b : `expr ${a} / ${b}`"
echo "a % b : `expr ${a} % ${b}`"
# 这里暂时用^符号来表示指数
echo "a^b : $((${a}**${b}))"
执行结果:
二、赋值运算符
运算符 | 说明 | 示例 |
---|---|---|
= | 赋值 | c=3 a=${c} 则a为3 |
+= | 加赋值 | 如果a为3,b为2,则((a+=b)) 后a为5,b为2 |
-= | 减赋值 | 如果a为3,b为2,则((a-=b)) 后a为1,b为2 |
*= | 乘赋值 | 如果a为3,b为2,则((a*=b)) 后a为6,b为2 |
/= | 除赋值 | 如果a为3,b为2,则((a/=b)) 后a为1,b为2 |
%= | 取模赋值 | 如果a为3,b为2,则((a%=b)) 后a为1,b为2 |
<<= | 左移位赋值 | 如果a为3,b为2,则((a<<=b)) 后a为12,b为2 |
>>= | 右移位赋值 | 如果a为3,b为2,则((a>>=b)) 后a为0,b为2 |
&= | 按位与赋值 | 如果a为3,b为2,则((a&=b)) 后a为2,b为2 |
|= | 按位或赋值 | 如果a为3,b为2,则((a|=b)) 后a为3,b为2 |
^= | 按位异或赋值 | 如果a为3,b为2,则((a^=b)) 后a为1,b为2 |
可以使用
(( ))
和let
命令进行运算,let
和(( ))
用法类似,都是用于整数运算。
示例如下:
#!/bin/bash
a=3
b=2
echo "a:${a},b:${b}"
((a+=b))
echo "a+=b : a:${a},b:${b}"
a=3
b=2
((a-=b))
echo "a-=b : a:${a},b:${b}"
a=3
b=2
((a*=b))
echo "a*=b : a:${a},b:${b}"
a=3
b=2
((a/=b))
echo "a/=b : a:${a},b:${b}"
a=3
b=2
((a%=b))
echo "a%=b : a:${a},b:${b}"
a=3
b=2
((a<<=b))
echo "a<<=b : a:${a},b:${b}"
a=3
b=2
((a>>=b))
echo "a>>=b : a:${a},b:${b}"
a=3
b=2
((a&=b))
echo "a&=b : a:${a},b:${b}"
a=3
b=2
((a|=b))
echo "a|=b : a:${a},b:${b}"
a=3
b=2
((a^=b))
echo "a^=b : a:${a},b:${b}"
执行结果:
三、关系运算符
在test
、[]
或[[ ]]
中使用的关系运算符:
运算符 | 说明 | 示例 |
---|---|---|
-eq | (equal)检测两个数是否相等,是返回0,否则返回1。 | 如果a为3,b为2,则[ ${a} -eq ${b} ] 返回1 |
-ne | (not equal)检测两个数是否不相等,是返回0,否则返回1。 | 如果a为3,b为2,则[ ${a} -ne ${b} ] 返回0 |
-gt | (greater than)检测左边的数是否大于右边的,是返回0,否则返回1。 | 如果a为3,b为2,则[ ${a} -gt ${b} ] 返回0 |
-lt | (less than)检测左边的数是否小于右边的,是返回0,否则返回1。 | 如果a为3,b为2,则[ ${a} -lt ${b} ] 返回1 |
-ge | (greater equal)检测左边的数是否大于等于右边的,是返回0,否则返回1。 | 如果a为3,b为2,则[ ${a} -ge ${b} ] 返回0 |
-le | (less equal)检测左边的数是否小于等于右边的,是返回0,否则返回1。 | 如果a为3,b为2,则[ ${a} -le ${b} ] 返回1 |
每一条Shell命令,在退出(运行结束)时都会返回一个比较小的整数值给调用它的程序,这就是命令的退出状态。一般来说,退出状态为0表示成功(true),非0表示失败(false),if语句的判定条件,从本质上判断的就是命令的退出状态。
[]
、[[ ]]
与里面的条件表达式之间要有空格,例如[${a}==${b}]
是错误的,必须写成[ ${a} == ${b} ]
。[]
和test
是等价的。
示例如下:
#!/bin/bash
a=3
b=2
echo "a:${a},b:${b}"
if [ ${a} -eq ${b} ]
then
echo "${a} -eq ${b} : a等于b"
else
echo "${a} -eq ${b} : a不等于b"
fi
if [ ${a} -ne ${b} ]
then
echo "${a} -ne ${b} : a不等于b"
else
echo "${a} -ne ${b} : a等于b"
fi
if [ ${a} -gt ${b} ]
then
echo "${a} -gt ${b} : a大于b"
else
echo "${a} -gt ${b} : a不大于b"
fi
if [ ${a} -lt ${b} ]
then
echo "${a} -lt ${b} : a小于b"
else
echo "${a} -lt ${b} : a不小于b"
fi
if [ ${a} -ge ${b} ]
then
echo "${a} -ge ${b} : a大于或等于b"
else
echo "${a} -ge ${b} : a小于b"
fi
if [ ${a} -le ${b} ]
then
echo "${a} -le ${b} : a小于或等于b"
else
echo "${a} -le ${b} : a大于b"
fi
执行结果:
在(())
中使用的关系运算符:
运算符 | 示例 | |
---|---|---|
== | 检测两个数是否相等,是返回0,否则返回1。 | 如果a为3,b为2,则((a==b)) 返回1 |
!= | 检测两个数是否不相等,是返回0,否则返回1。 | 如果a为3,b为2,则((a!=b)) 返回0 |
> | 检测左边的数是否大于右边的,是返回0,否则返回1。 | 如果a为3,b为2,则((a>b)) 返回0 |
>= | 检测左边的数是否大于等于右边的,是返回0,否则返回1。 | 如果a为3,b为2,则((a>=b)) 返回0 |
< | 检测左边的数是否小于右边的,是返回0,否则返回1。 | 如果a为3,b为2,则((a<b)) 返回1 |
<= | 检测左边的数是否小于等于右边的,是返回0,否则返回1。 | 如果a为3,b为2,则((a<=b)) 返回1 |
关系运算符只支持整数数字,不支持字符串,除非字符串的值是整数数字。注意
(( ))
中表达式的值不是0,返回状态才是0,否则返回状态是1,(( ))
进行整数比较的时候表达式的值与(())
返回状态正好相反,表达式的值为0,返回状态为1,表达式的值为1,返回状态为0。
在(( ))
中使用的示例如下:
#!/bin/bash
a=3
b=2
echo "a:${a},b:${b}"
if ((a==b))
then
echo "${a}==${b} : a等于b"
else
echo "${a}==${b} : a不等于b"
fi
if ((a!=b))
then
echo "${a}!=${b} : a不等于b"
else
echo "${a}!=${b} : a等于b"
fi
if ((a>b))
then
echo "${a}>${b} : a大于b"
else
echo "${a}>${b} : a不大于b"
fi
if ((a>=b))
then
echo "${a}>=${b} : a大于或等于b"
else
echo "${a}>=${b} : a小于b"
fi
if ((a<b))
then
echo "${a}<${b} : a小于b"
else
echo "${a}<${b} : a不小于b"
fi
if ((a<=b))
then
echo "${a}<=${b} : a小于或等于b"
else
echo "${a}<=${b} : a大于b"
fi
执行结果:
四、字符串运算符
可以在test
、[]
或[[ ]]
中使用:
运算符 | 说明 | 示例 |
---|---|---|
==、= | 如果两个字符串相等,则为true。 | 如果a为"expression",b为"expansion",则[[ ${a} == ${b} ]] 返回1 |
!= | 如果两个字符串不相等,则为true。 | 如果a为"expression",b为"expansion",则[[ ${a} != ${b} ]] 返回0 |
> | 如果左边字符串在字典顺序上排在右边字符串之后,则为true。 | 如果a为"expression",b为"expansion",则[[ ${a} > ${b} ]] 返回0 |
< | 如果左边字符串在字典顺序上排在右边字符串之前,则为true。 | 如果a为"expression",b为"expansion",则[[ ${a} < ${b} ]] 返回1 |
-z | 如果字符串的长度为零,则为true。 | 如果a为"expression",则[[ -z ${a} ]] 返回1 |
-n、string | 如果字符串的长度不为零,则为true。 | 如果a为"expression",则[[ -n ${a} ]] 或[[ ${a} ]] 返回0 |
注意:
- 在
test
、[]
或[[ ]]
中使用字符串运算符==
、=
、!=
、>
、<
只能比较字符串(只是字符串的值为数字时可以把它当成数字来比较,这只是字符串比较的结果与数值比较正好巧合,不推荐用它来比较数字,如[[ -3 < -2 ]]
返回1,[[ 03 < 2 ]]
返回0,[[ 1.5 == 1.50 ]]
返回1,都是错误结果。- 如果要使用
test
、[]
或[[ ]]
比较整数,需使用关系运算符-eq
、-ne
、-gt
、-lt
、-ge
、-le
,推荐在(( ))
中使用关系运算符==
、!=
、>
、<
、>=
、<=
进行整数比较。test
或[]
使用>
和<
需要加\
转义,[[ ]]
则不需要转义。- 使用
test
、[]
或[[ ]]
则没有>=
和<=
,可以通过[ ${a} \> ${b} -o ${a} == ${b} ]
或[[ ${a} > ${b} || ${a} == ${b} ]]
这种方式替代。test
或[]
会进行单词拆分,而[[ ]]
不会进行单词拆分。- 当与
[[ ]]
一起使用时,<
和>
使用当前区域设置按字典顺序排序,test
命令使用ASCII排序。
示例如下:
#!/bin/bash
a="expression"
b="expansion"
echo "a:${a},b:${b}"
if [[ ${a} == ${b} ]]
then
echo "${a} == ${b} : a等于b"
else
echo "${a} == ${b} : a不等于b"
fi
if [[ ${a} = ${b} ]]
then
echo "${a} = ${b} : a等于b"
else
echo "${a} = ${b} : a不等于b"
fi
if [[ ${a} != ${b} ]]
then
echo "${a} != ${b} : a不等于b"
else
echo "${a} != ${b} : a等于b"
fi
if [[ ${a} > ${b} ]]
then
echo "${a} > ${b} : a大于b"
else
echo "${a} > ${b} : a不大于b"
fi
if [[ ${a} > ${b} || ${a} == ${b} ]]
then
echo "${a} > ${b} || ${a} == ${b} : a大于或等于b"
else
echo "${a} > ${b} || ${a} == ${b} : a小于b"
fi
if [[ ${a} < ${b} ]]
then
echo "${a} < ${b} : a小于b"
else
echo "${a} < ${b} : a不小于b"
fi
if [[ ${a} < ${b} || ${a} == ${b} ]]
then
echo "${a} < ${b} || ${a} == ${b} : a小于或等于b"
else
echo "${a} < ${b} || ${a} == ${b} : a大于b"
fi
if [[ -z ${a} ]]
then
echo "-z ${a} : a长度为0"
else
echo "-z ${a} : a长度不为0"
fi
if [[ -n ${a} ]]
then
echo "-n ${a} : a长度不为0"
else
echo "-n ${a} : a长度为0"
fi
if [[ ${a} ]]
then
echo "${a} : a长度不为0"
else
echo "${a} : a长度为0"
fi
执行结果:
五、布尔运算符
运算符 | 说明 | 示例 |
---|---|---|
-a | 与运算,两个表达式都为true才返回true。 | 如果a为3,b为2,则[ ${a} -gt 1 -a ${b} -lt 1 ] 返回false |
-o | 或运算,有一个表达式为true,则返回true。 | 如果a为3,b为2,则[ ${a} -gt 1 -o ${b} -lt 1 ] 返回true |
! | 非运算,表达式为true,则返回false,否则返回true。 | 如果b为2,则[ ! ${b} -lt 1 ] 返回true |
(
-a
、-o
)必须在[]
中或配合test
命令使用。
示例如下:
#!/bin/bash
a=3
b=2
echo "a:${a},b:${b}"
if [ ${a} -gt 1 -a ${b} -lt 1 ]
then
echo "${a} -gt 1 -a ${b} -lt 1 : return true"
else
echo "${a} -gt 1 -a ${b} -lt 1 : return false"
fi
if [ ${a} -gt 1 -o ${b} -lt 1 ]
then
echo "${a} -gt 1 -o ${b} -lt 1 : return true"
else
echo "${a} -gt 1 -o ${b} -lt 1 : return false"
fi
if [ ! ${b} -lt 1 ]
then
echo "! ${b} -lt 1 : return true"
else
echo "! ${b} -lt 1 : return false"
fi
执行结果:
六、逻辑运算符
运算符 | 说明 | 示例 |
---|---|---|
&& | 逻辑与 | 如果a为3,b为2,则[[ ${a} > 1 && ${b} < 1 ]] 返回false |
|| | 逻辑或 | 如果a为3,b为2,则[[ ${a} > 1 || ${b} < 1 ]] 返回true |
! | 逻辑非 | 如果b为2,则[[ ! ${b} < 1 ]] 返回true |
&&
和||
可以在[[ ]]
或(( ))
中使用,不能在test
或[]
中使用,!
可以在[[ ]]
中使用,不能在(( ))
中使用。
示例如下:
#!/bin/bash
a=3
b=2
echo "a:${a},b:${b}"
if [[ ${a} > 1 && ${b} < 1 ]]
then
echo "${a} > 1 && ${b} < 1 : return true"
else
echo "${a} > 1 && ${b} < 1 : return false"
fi
if [[ ${a} > 1 || ${b} < 1 ]]
then
echo "${a} > 1 || ${b} < 1 : return true"
else
echo "${a} > 1 || ${b} < 1 : return false"
fi
if [[ ! ${b} < 1 ]]
then
echo "! ${b} < 1 : return true"
else
echo "! ${b} < 1 : return false"
fi
执行结果:
七、位运算符
运算符 | 说明 | 示例 |
---|---|---|
<< | 左移位 | ((3<<2)) 结果为12 |
>> | 右移位 | ((6>>2)) 结果为1 |
& | 按位与 | ((6&3)) 结果为2 |
| | 按位或 | ((6|3)) 结果为7 |
^ | 按位异或 | ((6^3)) 结果为5 |
~ | 按位非 | ((~6)) 结果为-7 |
示例如下:
#!/bin/bash
echo "3<<2 : $((3<<2))"
echo "6>>2 : $((6>>2))"
echo "6&3 : $((6&3))"
echo "6|3 : $((6|3))"
echo "6^3 : $((6^3))"
echo "~6 : $((~6))"
执行结果:
八、文件测试运算符
运算符 | 说明 | 示例 |
---|---|---|
-a file | 如果文件存在,则为true。 | [ -a ${file} ] |
-b file | 如果文件存在并且是个块设备文件,则为true。 | [ -b ${file} ] |
-c file | 如果文件存在并且是个字符设备文件,则为true。 | [ -c ${file} ] |
-d file | 如果文件存在并且是个目录,则为true。 | [ -d ${file} ] |
-e file | 如果文件存在,则为true。 | [ -e ${file} ] |
-f file | 如果文件存在并且是个普通文件(既不是目录也不是设备文件),则为true。 | [ -f ${file} ] |
-g file | 如果文件存在并且设置了set-group-id位,则为true。 | [ -g ${file} ] |
-h file、-L file | 如果文件存在并且是个符号链接,则为true。 | [ -h ${file} ] |
-k file | 如果文件存在并且设置了“sticky”位,则为true。 | [ -k ${file} ] |
-p file | 如果文件存在并且是个命名管道(FIFO),则为true。 | [ -p ${file} ] |
-r file | 如果文件存在并且可读,则为true。 | [ -r ${file} ] |
-s file | 如果文件存在并且大小大于0,则为true。 | [ -s ${file} ] |
-u file | 如果文件存在并且设置了set-user-id位,则为true。 | [ -u ${file} ] |
-w file | 如果文件存在并且可写,则为true。 | [ -w ${file} ] |
-x file | 如果文件存在并且可执行,则为true。 | [ -x ${file} ] |
-G file | 如果文件存在并且被有效组id所拥有,则为true。 | [ -G ${file} ] |
-N file | 如果文件存在并且自上次读取后被修改,则为true。 | [ -N ${file} ] |
-O file | 如果文件存在并且被有效用户id所拥有,则为true。 | [ -O ${file} ] |
-S file | 如果文件存在并且是个套接字,则为true。 | [ -S ${file} ] |
file1 -ef file2 | 如果file1和file2指向相同的设备和inode号,则为true。 | [ ${file1} -ef ${file2} ] |
file1 -nt file2 | 如果file1比file2更新(根据修改日期),或者file1存在而file2不存在,则为true。 | [ ${file1} -nt ${file2} ] |
file1 -ot file2 | 如果file1比file2更旧,或者file2存在而file1不存在,则为true | [ ${file1} -ot ${file2} ] |
除非另有说明,以上操作文件的表达式将跟随符号链接去操作其指向的目标,而不是链接本身。
示例如下:
有两个用来测试的文件,如上图所示,下面代码中只测试其中一部分运算符。
#!/bin/bash
file1="/root/test1.sh"
file2="/root/test2.sh"
if [[ -a ${file1} ]]
then
echo "-a ${file1} : file1存在"
else
echo "-a ${file1} : file1不存在"
fi
if [[ -b ${file1} ]]
then
echo "-b ${file1} : file1是块设备文件"
else
echo "-b ${file1} : file1不是块设备文件"
fi
if [[ -c ${file1} ]]
then
echo "-c ${file1} : file1是字符设备文件"
else
echo "-c ${file1} : file1不是字符设备文件"
fi
if [[ -d ${file1} ]]
then
echo "-d ${file1} : file1是目录"
else
echo "-d ${file1} : file1不是目录"
fi
if [[ -e ${file1} ]]
then
echo "-e ${file1} : file1存在"
else
echo "-e ${file1} : file1不存在"
fi
if [[ -f ${file1} ]]
then
echo "-f ${file1} : file1是普通文件"
else
echo "-f ${file1} : file1不是普通文件"
fi
if [[ -h ${file1} ]]
then
echo "-h ${file1} : file1是符号链接"
else
echo "-h ${file1} : file1不是符号链接"
fi
if [[ -L ${file1} ]]
then
echo "-L ${file1} : file1是符号链接"
else
echo "-L ${file1} : file1不是符号链接"
fi
if [[ -r ${file1} ]]
then
echo "-r ${file1} : file1可读"
else
echo "-r ${file1} : file1不可读"
fi
if [[ -s ${file1} ]]
then
echo "-s ${file1} : file1大小大于0"
else
echo "-s ${file1} : file1大小为0"
fi
if [[ -w ${file1} ]]
then
echo "-w ${file1} : file1可写"
else
echo "-w ${file1} : file1不可写"
fi
if [[ -x ${file1} ]]
then
echo "-x ${file1} : file1可执行"
else
echo "-x ${file1} : file1不可执行"
fi
if [[ -G ${file1} ]]
then
echo "-G ${file1} : file1被有效组id所拥有"
else
echo "-G ${file1} : file1没有被有效组id所拥有"
fi
if [[ -O ${file1} ]]
then
echo "-O ${file1} : file1被有效用户id所拥有"
else
echo "-O ${file1} : file1没有被有效用户id所拥有"
fi
if [[ -S ${file1} ]]
then
echo "-S ${file1} : file1是套接字"
else
echo "-S ${file1} : file1不是套接字"
fi
if [ ${file1} -nt ${file2} ]
then
echo "${file1} -nt ${file2} : file1比file2新"
else
echo "${file1} -nt ${file2} : file1不比file2新"
fi
if [ ${file1} -ot ${file2} ]
then
echo "${file1} -ot ${file2} : file1比file2旧"
else
echo "${file1} -ot ${file2} : file1不比file2旧"
fi
执行结果:
九、运算符优先级
以下运算符按优先级分组,优先级由下表所示,数字越低,优先级越高:
优先级 | 运算符 | 说明 |
---|---|---|
1 | id++、id– | 后增、后减 |
2 | ++id、–id | 先增、先减 |
3 | -、+ | 单目负号、单目正号 |
4 | !、~ | 逻辑非、按位非 |
5 | ** | 幂运算 |
6 | *、/、% | 乘、除、取模 |
7 | +、- | 加、减 |
8 | <<、>> | 左移位、右移位 |
9 | <=、>=、<、> | 比较 |
10 | ==、!= | 相等、不等 |
11 | & | 按位与 |
12 | ^ | 按位异或 |
13 | | | 按位或 |
14 | && | 逻辑与 |
15 | || | 逻辑或 |
16 | expr ? expr : expr | 条件运算符 |
17 | =、+=、-=、*=、/=、%=、<<=、>>=、&=、|=、^= | 赋值 |
18 | expr, expr | 逗号运算 |