三分薄地,认真耕耘

导航

 

expr命令可以实现数值运算、数值或字符串比较、字符串匹配、字符串提取、字符串长度计算等功能。还有具有几个具体功能,如:判断变量或参数是否为整数、是否为空、是否为0等。

1、expr命令

1.1 使用说明

1.1.1 字符串表达式

expr支持模式匹配和字符串操作。字符串表达式的优先级高于数值表达式和逻辑关系表达式。

  • STRING : REGEX

    • 执行模式匹配。两端参数会转换为字符格式,且第二个参数被视为正则表达式(GNU基本正则),它默认会隐含前缀"^"。随后将第一个参数和正则模式做匹配。
    • 如果匹配成功,且REGEX使用了'('和')',则此表达式返回匹配到的,如果未使用'('和')',则返回匹配的字符数。
    • 如果匹配失败,如果REGEX中使用了'('和')',则此表达式返回空字符串,否则返回为0。
    • 只有第一个'(...)'会引用返回的值;其余的'(...)'只在正则表达式分组时有意义。
    • 在正则表达式中,'+','?'和'|'分表代表匹配一个或多个,0个或1个以及两端任选其一的意思。
  • match STRING REGEX:等价于STRING : REGEX

  • substr STRING POSITION LENGTH:返回STRING字符串中从POSITION开始,长度最大为LENGTH的子串。如果POSITION或LENGTH为负数,0或非数值,则返回空字符串。

  • index STRING CHARSET:CHARSET中任意单个字符在STRING中最前面的字符位置。如果在STRING中完全不存在CHARSET中的字符,则返回0。见后文示例。

  • length STRING: 返回STRING的字符长度。

  • + TOKEN

    • 将TOKEN解析为普通字符串,即使TOKEN是像MATCH或操作符"/"一样的关键字。这使得expr length + "$x"'或'expr + "$x" : '.*/\(.\)'可以正常被测试,即使"$x"的值可能是'/'或'index'关键字。这个操作符是一个GUN扩展。
    • 通用可移植版的应该使用" $token" : ' \(.*\)'来代替'+ "$token"

1.1.2 算术表达式

expr支持普通的算术操作,算术表达式优先级低于字符串表达式,高于逻辑表达式

  • + -: 加减运算。两端参数会转换为整数,如果转换失败则报错。
  • * / %:乘,除,取模运算。两端参数会转换为整数,如果转换失败则报错。

1.1.3 逻辑表达式

expr支持普通的逻辑连接和逻辑关系。它的优先级最低。都需要使用转义符

  • |
    • 如果第一个参数非空且非0,则返回第一个参数的值,否则返回第二个参数的值,但要求第二个参数的值也是非空或非0,否则返回0。如果第一个参数是非空或非0时,不会计算第二个参数。
    • 经过测试,以上手册内容是错误的。正确的应该是:如果第一个参数非0,则返回第一个参数的值,否则返回第二个参数。但如果两个参数都为空,则报错。除非空字符串使用引号包围,此时将和0的处理方式一样。
[root@entos7 eval_test]# N=1
[root@entos7 eval_test]# M=0
[root@entos7 eval_test]# L=1
[root@entos7 eval_test]# expr $N \| $M 
# 第一个参数非0,返回第一个参数值
1
[root@entos7 eval_test]# echo $? 
# 返回码为0,成功
0
[root@entos7 eval_test]# expr $M \| $N
# 第一个参数为0, 返回第二个参数值
1
[root@entos7 eval_test]# echo $?
# 返回码为0,成功
0
[root@entos7 eval_test]# expr $M \| $M
# 两个参数都为0,返回0
0
[root@entos7 eval_test]# echo $?
# 两个参数都为0,返回码非0,失败
1

  • &
    • 如果两个参数都非空且非0,则返回第一个参数,否则返回0。如果第一个参为0或为空,则不会计算第二个参数。
    • 经过测试,以上手册内容是错误的。正确的应该是:如果两个参数都非0,则返回第一个参数,否则返回0。但任意一个参数为空,则报错。除非空字符串使用引号包围,此时将和0的处理方式一样。
[root@entos7 eval_test]# N=a
[root@entos7 eval_test]# M=0
[root@entos7 eval_test]# L=C
[root@entos7 eval_test]# expr $N \& $L
# 两个参数都非空且非0,返回第一个参数
a
[root@entos7 eval_test]# echo $?
# 执行返回码为0,成功
0
[root@entos7 eval_test]# expr $N \& $M
# 第二个参数或第一个参数为0或空,则返回0
0
[root@entos7 eval_test]# echo $?
# 执行返回码为1,失败
1
[root@entos7 eval_test]# expr $M \& $N
# 第二个参数或第一个参数为0或空,则返回0
0
[root@entos7 eval_test]# echo $?
# 执行返回码为1,失败
1

  • < <= = == != >= >
    比较两端的参数,如果为true,则返回1,否则返回0。"=="是"="的同义词。"expr"首先尝试将两端参数转换为整数,并做算术比较,如果转换失败,则按字符集排序规则做字符比较。

括号'()'可以改变优先级,但使用时需要使用反斜线对括号进行转义。类似expr \($N \> $M\) \| \($N \!\= $L\)

1.1.4 简单示例

1、将shell中变量'foo'的值增加1:

 foo=$(expr $foo + 1)

2、输出变量路径变量'$fname'中不包含'/'的文件名部分:

expr $fname : '.*/\(.*\)' '|' $fname

#解释:其中的'|'是expr中的连接符,只不过是被引号包围防止被shell解析。例如$fname=/etc/hosts,则此表达式返回hosts,如果$fname=/usr/share/,则此表达式'|'的左边为空,所以返回'|'右边的值,即$fname,即返回/usr/share/。如下所示

[root@entos7 eval_test]# fname=/usr
[root@entos7 eval_test]# expr $fname : '.*/\(.*\)' '|' $fname
usr
[root@entos7 eval_test]# fname=/usr/bin
[root@entos7 eval_test]# expr $fname : '.*/\(.*\)' '|' $fname
bin
[root@entos7 eval_test]# fname=/usr/bin/
[root@entos7 eval_test]# expr $fname : '.*/\(.*\)' '|' $fname
/usr/bin/

3、expr匹配返回示例


[root@entos7 eval_test]# str=aaa
[root@entos7 eval_test]# expr $str : 'a\+' 
#因为REGEX部分没有使用\(\),所以返回匹配的字符数
3
[root@entos7 eval_test]# expr $str : 'a\(.\)a'
#REGEX部分使用了\(\),所以返回匹配的字符
a
[root@entos7 eval_test]# expr index abcdef cf
3
[root@entos7 eval_test]# expr index index cf
#因为第二个index是关键字,所以报格式错误
expr: syntax error
[root@entos7 eval_test]# expr index + index b
#使用+将index关键字解析为普通字符串
0

1.2、expr高级使用

  • (1).数值表达式("+ - * / %")和比较表达式("< <= = == != >= >")会先将两端的参数转换为数值,转换失败将报错。所以可借此来判断参数或变量是否为整数。

  • (2).expr中的很多符号需要转义或使用引号包围。

  • (3).所有操作符的两边,都需要有空格。

1.2.1 **string : REGEX**字符串匹配示例

要输出匹配到的字符串结果,需要使用"\(""\)",否则返回的将是匹配到的字符串数量(长度)。

# 使用"\("和"\)",返回匹配到的字符串
[root@entos7 expr_test]# expr abcdef : 'ab\(.*\)ef'
cd
[root@entos7 expr_test]# expr abcdef : 'ab\(.*\)'
cdef
[root@entos7 expr_test]# expr abcdef : 'ab\(.\?\)'
c
[root@entos7 expr_test]# expr abcdef : 'ab.*'
6
[root@entos7 expr_test]# expr abcdef : 'ab.'
3

注意,由于REGEX中隐含了"^",所以使得匹配时都是从string首字符开始的。


[root@entos7 expr_test]# expr abcdef : 'cd.'
0

之所以为0,是因为真正的正则表达式是"^cd.",而abcde不是c开头而是a开头的,所以无法匹配到任何结果。因此,任何字符串匹配时,都应该从首字符开始。

字符串匹配时,会先将两端参数转换为字符格式。

1.2.2 index string chars用法示例

该表达式是从string中搜索chars中某个字符的位置,然后返回所有chars中字符在string中最靠前的位置。例如:


[root@entos7 expr_test]# expr index 'abcdef' 'dec'
3

#该命令将对字符串"dec"逐字符分解,首先分解得到第一个字符d,从abcde中搜索到d的位置为4,再分解得到第二个字符e,该字符在abcde中的位置为5,最后得到的字符是c,该字符在abcde中的位置为3。其中3是最靠前的字符,所以命令返回的结果为3。

[root@entos7 expr_test]# expr index 'abcdef' 'xfc'
3

#如果chars中的所有字符都不存在于string中,则返回0
[root@entos7 expr_test]# expr index 'abcdef' 'hig'
0

1.2.3 substr string pos len用法示例

该表达式是从string中取出从pos位置开始长度为len的子字符串。如果pos或len为非正整数时,将返回空字符串。


[root@entos7 expr_test]# expr substr abcdef 0 2

[root@entos7 expr_test]# expr substr abcdef 1 2
ab
[root@entos7 expr_test]# expr substr abcdef 3 6
cdef
[root@entos7 expr_test]# expr substr abcdef 2 0

[root@entos7 expr_test]# expr substr abcdef 2 -1


1.2.4 length string用法示例

该表达式是返回string的长度,其中string不允许为空,否则将报错,所以可以用来判断变量是否为空。

[root@entos7 expr_test]# expr length abcde
5
[root@entos7 expr_test]# expr length $ccc
expr: syntax error
[root@entos7 expr_test]# if [ $? -ne 0 ];then echo '$ccc is null';fi
$ccc is null

1.2.5 + token用法示例

expr中有些符号和关键字有特殊意义,比如'match','index','length',如果要让其成为字符,使用该表达式将任意token强制解析为普通字符


[root@entos7 expr_test]# expr index index d
expr: syntax error
[root@entos7 expr_test]# expr index + index d
3
[root@entos7 expr_test]# expr length length
expr: syntax error
[root@entos7 expr_test]# expr length + length
6

1.2.6 算术运算用法示例

算术符号两边都要有空格,否则会报格式错误


[root@entos7 expr_test]# expr 1+2
1+2
[root@entos7 expr_test]# expr 1 +2
expr: syntax error
[root@entos7 expr_test]# expr 1+ 2
expr: syntax error
[root@entos7 expr_test]# expr 1 + 2
3
[root@entos7 expr_test]# expr $a + $b
7
[root@entos7 expr_test]# expr $a + 4
7

算术乘法符号"*"因为是shell的元字符,所以要转义,可以使用引号包围,或者使用反斜线。


[root@entos7 expr_test]# a=4
[root@entos7 expr_test]# b=3
[root@entos7 expr_test]# expr $a \* $b
12
[root@entos7 expr_test]# expr $a / $b
1
[root@entos7 expr_test]# expr $a % $b
1

由于expr在进行算术运算时,首先会将操作符两边的参数转换为整数,任意一端转换失败都将会报错,所以可以用来判断参数或变量是否为整数。

1.2.7 比较操作符

**< <= = == != >= >**用法示例。其中"<"和">"是正则表达式正的锚定元字符,且"<"会被shell解析为重定向符号,所以需要转义或用引号包围。

这些操作符会首先会将两端的参数转换为数值,如果转换成功,则采用数值比较,如果转换失败,则按照字符集的排序规则进行字符大小比较。比较的结果若为true,则expr返回1,否则返回0。


[root@entos7 expr_test]# a=3
[root@entos7 expr_test]# expr $a = 1
0
[root@entos7 expr_test]# expr $a > 1
[root@entos7 expr_test]# expr $a \> 1
1
[root@entos7 expr_test]# expr $a \>= 1
1
[root@entos7 expr_test]# expr ($a \* 3) = 9
[root@entos7 expr_test]# expr abc \> ab
1

2、(())命令

2.1 使用方法

(1)双小括号 (( )) 是 Bash Shell 中专门用来进行整数运算的命令,它的效率很高,写法灵活,是企业运维中常用的运算命令。(( )) 只能进行整数运算,不能对小数(浮点数)或者字符串进行运算。后续讲到的 bc 命令可以用于小数运算。

(2)(( )) 的用法格式如下,即把数学运算表达式放在(())里面。

((表达式))

    • 表达式可以有多个,以逗号分隔,并以最后一个表达式的值作为 (( )) 命令的执行结果。
  • “(( )) 命令的执行结果”与“(())命令的退出状态”是不同的概念,前者类似于C语言中函数的返回值,后者表示一个命令的退出状态(0表示成功,其他数字表示失败)。
  • 可以使用$获取 (( )) 命令的执行结果,这和使用$获得变量值是类似的。$((表达式))指的是表达式的值,而非(())命令的退出状态。
  • 在 (( )) 中使用变量无需加上$前缀,(( )) 会自动解析变量名。

2.2 使用方法示例

操作符,命令 说明
((a=10+66)
((b=a-15))
((c=a+b))
这种写法可以在计算完成后给变量赋值。以 ((b=a-15)) 为例,即将 a-15 的运算结果赋值给变量 b。
注意,使用变量时不用加$前缀,(( )) 会自动解析变量名。
a=$((10+66)
b=$((a-15))
c=$((a+b))
可以在 (( )) 前面加上$符号获取 (( )) 命令的执行结果,即获取整个表达式的值。以 c=$((a+b)) 为例,即将 a+b 这个表达式的运算结果赋值给变量 c。注意,类似 c=((a+b)) 这样的写法是错误的,不加$就不能取得表达式的结果。
((a>7 && b==c)) (( )) 内部也可以进行逻辑运算(与或非),在 if 语句中常会使用逻辑运算。
echo $((a+10)) 需要立即输出表达式的运算结果时,可以在 (( )) 前面加$符号。
((a=3+5, b=a+10)) 对多个表达式同时进行计算。

2.3 示例演示


[root@entos7 expr_test]# echo $((a=3+5, b=a+10))
18
[root@entos7 expr_test]# echo $((3<8))
1
[root@entos7 expr_test]# ((3<8))
[root@entos7 expr_test]# echo $?
0
#注意,$((expression))指的是expression的值,而非(())命令的退出状态。
    
#表达式3<8是判断语句,该表达式的值为1,而$(())表示表达式的值,所以echo $((3<8))输出为1


#在(())中使用变量时不用加$,(())会自动解析变量
[root@entos7 expr_test]# echo $((a++))
10
[root@entos7 expr_test]# echo $((++a))
12
[root@entos7 expr_test]# echo $a
12
[root@entos7 expr_test]# b=$((1+2**3-4%3))
[root@entos7 expr_test]# echo $b
8

3、let命令

3.1 用法说明

(1)let 命令和双小括号 (( )) 的用法是类似的,它们都是用来对整数进行运算,不能对小数(浮点数)或者字符串进行运算。

(2)let命令的用法格式有三种形式:


let 表达式
let '表达式'
let "表达式"

  • 当表达式中含有特殊字符(例如 |)时,需要用双引号或者单引号将表达式包围起来。
  • 表达式可以有多个,以空格分隔,并且以最后一个表达式的值作为let 命令的执行结果。

(3)对于类似let x+y这样的写法,Shell 虽然计算了 x+y 的值,但却将结果丢弃;若不想这样,可以使用let sum=x+y将 x+y 的结果保存在变量 sum 中。

3.2 实例演示


[root@entos7 expr_test]# a=10 b=20
[root@entos7 expr_test]# echo $((a+b))
30
[root@entos7 expr_test]# echo let a+b
let a+b
[root@entos7 expr_test]# echo $(let a+b)

[root@entos7 expr_test]# let sum=a+b
[root@entos7 expr_test]# echo $sum
30

4、$[]命令

4.1 用法

(1)和 (())、let 命令类似,$[] 也只能进行整数运算。

(2)$[ ]用法如下所示。

$[表达式]

  • $[ ] 会对表达式进行计算,并取得计算结果。
  • 如果表达式中包含了变量,那么你可以加$,也可以不加。
  • 不能单独使用 $[],必须能够接收 $[] 的计算结果

4.2 实例演示


[root@entos7 expr_test]# echo $[3*5] #直接输出结算结果
15
[root@entos7 expr_test]# echo $[(3+5)*5] #使用()
40
[root@entos7 expr_test]# n=6
[root@entos7 expr_test]# echo $[n*2] # 表达式中的变量可以加$,也可以不加
12
[root@entos7 expr_test]# echo $[$n*2]
12
[root@entos7 expr_test]# $[3+4] #不能单独使用 $[],必须能够接收 $[] 的计算结果
bash: 7: command not found...

5、bc命令

Bash Shell 内置了对整数运算的支持,但是并不支持浮点运算,而 Linux bc 命令可以很方便的进行浮点运算,当然也支持整数运算。bc 甚至可以称得上是一种编程语言了,它支持变量、数组、输入输出、分支结构、循环结构、函数等基本的编程元素。


[root@entos7 expr_test]# bc -h
usage: bc [options] [file ...]
  -h  --help         print this usage and exit
  -i  --interactive  force interactive mode
  -l  --mathlib      use the predefined math routines
  -q  --quiet        don't print initial banner
  -s  --standard     non-standard bc constructs are errors
  -w  --warn         warn about non-standard bc constructs
  -v  --version      print version information and exit

5.1 在交互模式使用bc命令

(1) 直接在shell中以交互模式使用bc


[root@entos7 expr_test]# 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'.

2*22 #直接输入数学计算式子,按回车可得到计算结果
44
3^2
9
n=19 #bc支持变量
n++
19
++n
21
(3*9)%4 
3
quit #输入quit并回车后退出bc程序

(3)除了变量,bc 还支持函数、循环结构、分支结构等常见的编程元素,它们和其它编程语言的语法类似。下面我们定义一个求阶乘的函数:


[root@entos7 expr_test]# bc -q
define func(x){
    if ( x<=1 )
      return 1;
return x*func(x-1)
}
func(5)
120
quit

(4)bc 有四个内置变量,我们在计算时会经常用到,如下表所示。

scale 指定精度,也即小数点后的位数;默认为 0,也即不使用小数部分。
ibase 指定输入的数字的进制,默认为十进制。
obase 指定输出的数字的进制,默认为十进制。
last 或者 . 表示最近打印的数字

23*2
46
obase=16
23*2
2E
obase=10
ibase=16
10*10
256
obase=2
ibase=10
16*16
100000000
quit

5)除了内置变量,bc 还有一些内置函数,如下表所示:

函数名 作用
s(x) 计算 x 的正弦值,x 是弧度值。
c(x) 计算 x 的余弦值,x 是弧度值。
a(x) 计算 x 的反正切值,返回弧度值。
l(x) 计算 x 的自然对数。
e(x) 求 e 的 x 次方。
j(n, x) 贝塞尔函数,计算从 n 到 x 的阶数。

要想使用这些数学函数,在输入 bc 命令时需要使用-l选项,表示启用数学库。


[root@entos7 expr_test]# bc -q -l
x=5
s(x)
-.95892427466313846889
e(x)
148.41315910257660342111
quit

5.2 在shell中使用bc

在 Shell 编程中,我们也可以通过管道和输入重定向来使用 bc。

管道是 Linux 进程间的一种通信机制,它可以将前一个命令(进程)的输出作为下一个命令(进程)的输入,两个命令之间使用竖线|分隔。
通常情况下,一个命令从终端获得用户所输入的内容,如果让它从其他地方(比如文件)获得输入,那么就需要重定向。

5.2.1 借助管道符使用bc

1、使用方法


echo "expression" | bc

  • expression就是希望计算的数学表达式,它必须符合 bc 的语法。
  • 在 expression 中,还可以使用 Shell 脚本中的变量。

2、示例


[root@entos7 expr_test]# echo '3*8' |bc
24
[root@entos7 expr_test]# ret=$(echo '3+8'|bc)
[root@entos7 expr_test]# echo $ret
11
[root@entos7 expr_test]# echo "scale=4;3*8/7"|bc
3.4285
[root@entos7 expr_test]# x=4
[root@entos7 expr_test]# echo "scale=5;n=$x+2;e(n)"|bc
#没有使用-l参数调用数学库,无法识别到e(n),报错
Runtime error (func=(main), adr=20): Function e not defined.
[root@entos7 expr_test]# echo "scale=5;n=$x+2;e(n)"|bc -l

5.2.2 借助输入重定向使用bc命令

1、使用方法


variable=$(bc << EOF
expressions
EOF
)

  • variable是变量名,express是要计算的数学表达式(可以换行,与进入bc后的书写形式一样)。
  • EOF是数学表达式的开始和结束标识(可以换成其它的名字,比如 aaa、bbb 等)。

2、使用实例

[root@entos7 expr_test]# m=1E
[root@entos7 expr_test]# n=$(bc<< EOF
> obase=10
> ibase=16
> print $m
> EOF
> )
[root@entos7 expr_test]# echo $n
30

特别感谢 :本文为学习记录使用,主要参考了

SHELL脚本--expr命令全解

Shell编程——数学计算命令

在此特别感谢。

posted on 2025-05-26 21:17  平复心态  阅读(63)  评论(0)    收藏  举报