首页 Shell 运算符
文章
取消

Shell 运算符

算数运算符

bash 支持 4 种语法来进行算数运算(只支持整数):let(())$(())$[](过时,同 $(()))。

let(())$(()) 这 3 个都是内置命令,它们所支持的运算符是一样的,那么它们有什么区别呢?

  • let:支持多个算数表达式的计算(单纯计算)
  • (()):只支持单个算数表达式的计算(单纯计算)
  • $(()):只支持单个算数表达式的计算(计算&结果替换)

let 的基本用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 如果需要在算数表达式中引用变量,请不要在变量名前添加 '$' 美元符
# 建议将运算表达式用引号(单双引号都行)括起来,当作一个参数传递给 let
# 这样的好处是允许在表达式中间存在空白符(空格),否则不允许空白符的存在

# 加、减、乘、除、取余、乘方
let "result = a + b"
let "result = a - b"
let "result = a * b"
let "result = a / b"
let "result = a % b"
let "result = a ** b"

let "a += b"
let "a -= b"
let "a *= b"
let "a /= b"
let "a %= b"
let "a **= b" # 错误

let "++a"   # 前自增
let "--a"   # 前自减
let "a++"   # 后自增
let "a--"   # 后自减

前自增、后自增区别(自减同理)

1
2
3
4
5
6
7
8
9
10
11
$ a=10              # 初始化为 10

$ let "ret = ++a"   # 前自增,返回自增后的结果
$ typeset a ret     # 这是zsh语法,如果是bash,请改为`echo $变量名`形式
a=11
ret=11

$ let "ret = a++"   # 后自增,返回自增前的结果
$ typeset a ret     # 这是zsh语法,如果是bash,请改为`echo $变量名`形式
a=12
ret=11

let 支持同时运算多个表达式

1
2
3
4
5
6
7
8
9
$ a=20; b=10
$ let "r1 = a + b" "r2 = a - b" "r3 = a * b" "r4 = a / b"
$ typeset a b r1 r2 r3 r4   # 这是zsh语法,如果是bash,请改为`echo $变量名`形式
a=20
b=10
r1=30
r2=10
r3=200
r4=2
(())
支持的算术表达式基本同 let,只是不支持多个表达式计算。
$(())
(()) 的基础上增加了 结果替换,如 result=$((10 * 10)),result变量的值为100。

let(()) 是有返回值的,或者叫退出码exit-code

  • 最后一个表达式的值!=0:执行结果为,即退出码为 0
  • 最后一个表达式的值==0:执行结果为,即退出码为 1

let 支持的运算符(优先级从高到低):

  • var++var--:后自增、后自减
  • ++var--var:前自增、前自减
  • +expr-expr:一元加(乘以 1)、一元减(乘以 -1)
  • !~:逻辑非、按位非,! 建议放在单引号中(let)
  • **:幂(乘方)
  • */%:乘、除、取余
  • +-:加、减
  • <<>>:按位左移、按位右移
  • <<=>>=:小于、小于等于、大于、大于等于
  • ==!=:等于、不等于
  • &:按位与
  • ^:按位异或
  • |:按位或
  • &&:逻辑与
  • ||:逻辑非
  • expr1 ? expr2 : expr3:条件运算符,如果 expr1 为 true 则计算 expr2,如果 expr1 为 false 则计算 expr3
  • =+=-=*=/=%=<<=>>=&=^=|=:赋值、加减乘除取余、左移右移、按位与、按位异或、按位或 等特殊赋值运算符,类似其他编程语言,如a+=10等同于a=a+10

let 系列的操作符只支持整数运算(即使计算结果是小数,也会被去除小数部分,注意不是四舍五入)。

如果需要小数运算(浮点数运算),请使用 awk、bc 等外部命令来完成,建议使用 awk,bc 有些系统没有。

script.awk

1
2
3
4
5
6
7
8
9
10
BEGIN {
    a = 45; b = 20;
    printf("a + b = %d\n", a + b);
    printf("a - b = %d\n", a - b);
    printf("a * b = %d\n", a * b);
    printf("a / b = %g\n", a / b);
    printf("a % b = %d\n", a % b);
    printf("a ^ b = %d\n", a ^ b);
    printf("a ** b = %d\n", a ** b);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ awk -f script.awk
a + b = 65
a - b = 25
a * b = 900
a / b = 2.25
a % b = 5
a ^ b = 1159445329576199501472242656608256
a ** b = 1159445329576199501472242656608256

$ awk 'BEGIN { print(3 / 2) }'
1.5

$ awk 'BEGIN { print(5 / 3) }'
1.66667

$ awk 'BEGIN { printf("%.6f\n", 5 / 3) }'
1.666667

关系运算符

这里说的关系运算符是关于数字(整数)的,字符串的在后面。

在 bash 中,需要借助/bin/test/bin/[命令进行关系运算。

test[是一样的,它们都是普通命令,区别是[需要使用]标记结束。

如:test 10 -eq 10[ 10 -eq 10 ],这也就是为什么[]内侧需要空格。

建议使用 [ 替代 test 命令,因为 bash 已经内置了 [ 命令,所以效率更高。

现代 bash 也内置了 test 命令,所以没区别,但还是习惯用 [,因为更短。

注意,在 shell 中,返回值 0 表示真,其他值表示假,这个和其他语言是相反的。

运算符说明例子
-eqequal==等于[ 10 -eq 10 ],真
-nenot equal!=不等于[ 10 -ne 11 ],真
-ltless than<小于[ 10 -lt 20 ],真
-leless than or equal<=小于等于[ 10 -le 10 ],真
-gtgreater than>大于[ 10 -gt 5 ],真
-gegreater than or equal>=大于等于[ 10 -ge 10 ],真

逻辑运算符

同样的,逻辑运算也要借助于/bin/test/bin/[命令。

运算符说明例子
-a逻辑与,&&[ 10 -eq 10 -a 20 -gt 10 ],真
-o逻辑或,||[ 10 -eq 10 -o 20 -lt 10 ],真
!逻辑非,![ 10 -eq 10 -a ! 20 -lt 10 ],真

如果你喜欢使用&&||!,那么你可以尝试使用[[关键字:

如:[[ 10 -eq 10 && 20 -eq 20 ]]真、[[ 1 -lt 0 || 1 -gt 0 ]]真。

但是,[[ ]]中不再支持-a-o,只支持&&||!了。

字符串测试

同样的,字符串测试也要借助于/bin/test/bin/[命令。

运算符说明例子
=两个字符串是否 相等[ "a" = "a" ],真
!=两个字符串是否 不相等[ "a" != "b" ],真
-n是否为 非空 字符串[ -n "www" ],真
STRING是否为 非空 字符串,同-n[ "www" ],真
-z是否为 字符串[ -z "" ],真

文件测试

同样的,文件测试也要借助于/bin/test/bin/[命令。

运算符说明例子
-e文件是否存在[ -e /etc/resolv.conf ],真
-s文件是否非空[ -s /etc/resolv.conf ],真
-d文件是否为目录[ -d /etc ],真
-f文件是否为普通文件[ -f /etc/resolv.conf ],真
-b文件是否为块设备[ -b /dev/sda ],真
-c文件是否为字符设备[ -c /dev/tty ],真
-p文件是否为具名管道[ -p pipe ],pipe 是我创建的管道文件,真
-S文件是否为套接字文件[ -S /run/systemd/coredump ],真
-h文件是否为软链接文件[ -h /bin/sh ],真
-L文件是否为软链接文件,同-h[ -L /bin/sh ],真
-r文件是否有可读权限[ -r /etc/resolv.conf ],真
-w文件是否有可写权限[ -w /etc/resolv.conf ],真
-x文件是否有可执行权限[ -x /bin/sh ],真
-u文件是否有SUID权限-
-g文件是否有SGID权限-
-k文件是否有sticky权限-
-O文件所属用户是否有效-
-G文件所属用户组是否有效-
-t文件描述符是否已打开[ -t 0 ],真
-ef两个文件是否相同(所在设备相同且 inode 相同)[ f1 -ef f2 ],f2 是 f1 的硬连接文件,真
-ntnewer than(最近修改时间)[ f1 -nt f2 ],假
-otolder than(最近修改时间)[ f1 -ot f2 ],假
本文由作者按照 CC BY 4.0 进行授权

Shell 位置参数

Shell 打印输出