MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Bash中的数学运算与表达式求值

2022-09-062.0k 阅读

Bash中的数学运算基础

在Bash脚本编程中,数学运算虽然不像在专门的编程语言(如Python、C++)中那样丰富和灵活,但也提供了基本的数学运算功能,能够满足许多常见的脚本需求。Bash的数学运算主要依赖于两种方式:使用 expr 命令和使用 $((...)) 结构。

使用 expr 命令进行数学运算

expr 是一个用于计算的工具,它可以执行整数的加、减、乘、除和求余等基本运算。以下是 expr 命令的基本语法:

expr num1 operator num2

其中 num1num2 是要进行运算的整数,operator 是运算符。

加法运算

加法运算符为 +。例如,计算 3 + 5,可以这样写:

result=$(expr 3 + 5)
echo $result

在上述代码中,通过 expr 命令计算 3 + 5 的结果,并将结果赋值给变量 result,最后通过 echo 命令输出结果。需要注意的是,在 expr 命令中,运算符前后需要有空格,否则会导致错误。

减法运算

减法运算符为 -。例如,计算 10 - 4

result=$(expr 10 - 4)
echo $result

同样,运算符 - 前后需要有空格。

乘法运算

乘法运算符为 *。但在Bash中,* 在文件名扩展中有特殊含义,所以在 expr 命令中使用乘法时,需要对 * 进行转义,即 \*。例如,计算 6 * 7

result=$(expr 6 \* 7)
echo $result

如果不进行转义,Bash可能会将 * 解释为文件名通配符,导致错误。

除法运算

除法运算符为 /。例如,计算 20 / 4

result=$(expr 20 / 4)
echo $result

这里得到的结果是整数部分,因为 expr 进行的是整数除法。如果要得到小数结果,expr 无法直接实现,需要借助其他工具(如 bc,后续会介绍)。

求余运算

求余运算符为 %。例如,计算 17 % 5

result=$(expr 17 % 5)
echo $result

expr 命令虽然能完成基本的整数运算,但它的语法相对繁琐,特别是在处理复杂表达式时。而且它仅支持整数运算,对于浮点数运算无能为力。

使用 $((...)) 结构进行数学运算

$((...)) 是Bash中更简洁且功能更强大的数学运算结构。它不仅支持基本的整数运算,还支持一些扩展的运算和更灵活的表达式书写。

基本运算

使用 $((...)) 进行基本运算非常简单。例如,计算 4 + 9

result=$((4 + 9))
echo $result

expr 命令不同,$((...)) 结构不需要在运算符前后添加空格,书写更加简洁。

减法运算,如计算 15 - 7

result=$((15 - 7))
echo $result

乘法运算,计算 8 * 3

result=$((8 * 3))
echo $result

这里无需像 expr 那样对 * 进行转义。

除法运算,计算 30 / 5

result=$((30 / 5))
echo $result

求余运算,计算 23 % 6

result=$((23 % 6))
echo $result

复合表达式

$((...)) 结构允许编写更复杂的复合表达式。例如,计算 (3 + 5) * 2

result=$(((3 + 5) * 2))
echo $result

在这个表达式中,先计算括号内的 3 + 5,再将结果乘以 2

还可以在表达式中使用变量。假设有两个变量 ab,计算 (a + b) * (a - b)

a=7
b=3
result=$(((a + b) * (a - b)))
echo $result

这里先根据变量 ab 的值计算括号内的表达式,然后再进行乘法运算。

自增和自减运算

$((...)) 结构支持自增(++)和自减(--)运算符。自增运算符有两种形式:前置自增(++var)和后置自增(var++)。前置自增先将变量的值加1,然后返回加1后的值;后置自增先返回变量的当前值,然后再将变量的值加1。

例如,前置自增:

num=5
result=$((++num))
echo $result  # 输出 6
echo $num    # 输出 6

后置自增:

num=5
result=$((num++))
echo $result  # 输出 5
echo $num    # 输出 6

自减运算符同理,前置自减(--var)先减1再返回值,后置自减(var--)先返回值再减1。

处理浮点数运算

前面介绍的 expr$((...)) 结构主要用于整数运算。在实际应用中,有时需要进行浮点数运算。Bash本身对浮点数运算的支持有限,但可以借助外部工具 bc 来实现。

bc 工具简介

bc 是一个基本的计算器语言,它支持高精度的算术运算,包括整数和浮点数运算。bc 可以从标准输入读取表达式并输出结果,也可以读取脚本文件进行批处理。

使用 bc 进行浮点数运算

要在Bash脚本中使用 bc 进行浮点数运算,可以通过管道将表达式传递给 bc。例如,计算 2.5 + 3.7

result=$(echo "2.5 + 3.7" | bc)
echo $result

在上述代码中,通过 echo 命令将表达式 "2.5 + 3.7" 输出到标准输出,然后通过管道(|)将其传递给 bcbc 计算表达式并输出结果,最后将结果赋值给变量 result 并输出。

设置小数精度

bc 默认输出的小数精度有限。可以通过设置 scale 参数来指定小数精度。例如,要将结果保留两位小数,计算 5.678 / 2.34

result=$(echo "scale=2; 5.678 / 2.34" | bc)
echo $result

在表达式中,先通过 scale=2 设置小数精度为2,然后进行除法运算。

使用变量

bc 中也可以使用变量。例如:

a=4.5
b=2.1
result=$(echo "scale=3; $a * $b" | bc)
echo $result

这里先定义了两个变量 ab,然后在 bc 表达式中使用这两个变量进行乘法运算,并设置小数精度为3。

复杂表达式

bc 支持复杂的数学表达式,包括三角函数、对数等。但要使用这些功能,需要在 bc 中引入 math 库。例如,计算 sin(30°)(注意 bc 中三角函数的参数是弧度,30° 转换为弧度约为 30 * 3.14159265358979323846 / 180):

result=$(echo "scale=4; s(30 * 3.14159265358979323846 / 180)" | bc -l)
echo $result

这里通过 bc -l 引入 math 库(-l 选项表示使用 math 库),然后计算正弦值,并设置小数精度为4。

位运算

除了基本的算术运算和浮点数运算,Bash还支持位运算。位运算是对二进制位进行操作,常用于底层编程和系统管理任务,如权限设置、设备控制等。

按位与(&

按位与运算符 & 对两个数的二进制位进行与操作。只有当两个对应的二进制位都为1时,结果位才为1,否则为0。

例如,计算 5 & 3

num1=5  # 二进制 101
num2=3  # 二进制 011
result=$((num1 & num2))
echo $result  # 结果为 1,二进制 001

在上述代码中,先将 53 转换为二进制,然后按位进行与操作,最后将结果转换回十进制并输出。

按位或(|

按位或运算符 | 对两个数的二进制位进行或操作。只要两个对应的二进制位中有一个为1,结果位就为1,只有当两个对应的二进制位都为0时,结果位才为0。

例如,计算 5 | 3

num1=5  # 二进制 101
num2=3  # 二进制 011
result=$((num1 | num2))
echo $result  # 结果为 7,二进制 111

按位异或(^

按位异或运算符 ^ 对两个数的二进制位进行异或操作。当两个对应的二进制位不同时,结果位为1,相同时结果位为0。

例如,计算 5 ^ 3

num1=5  # 二进制 101
num2=3  # 二进制 011
result=$((num1 ^ num2))
echo $result  # 结果为 6,二进制 110

按位取反(~

按位取反运算符 ~ 对一个数的二进制位进行取反操作。将0变为1,1变为0。

例如,计算 ~5

num=5  # 二进制 101
result=$((~num))
echo $result  # 结果为 -6,在有符号整数表示中,5 的二进制补码取反得到 -6 的二进制补码

需要注意的是,在Bash中,按位取反的结果是基于有符号整数的二进制补码表示。

左移(<<

左移运算符 << 将一个数的二进制位向左移动指定的位数。每左移一位,相当于该数乘以2。

例如,将 3 左移2位:

num=3  # 二进制 011
result=$((num << 2))
echo $result  # 结果为 12,二进制 1100

这里 3(二进制 011)左移2位后变为 1100,即十进制的 12

右移(>>

右移运算符 >> 将一个数的二进制位向右移动指定的位数。每右移一位,相当于该数除以2并向下取整。

例如,将 12 右移2位:

num=12  # 二进制 1100
result=$((num >> 2))
echo $result  # 结果为 3,二进制 011

12(二进制 1100)右移2位后变为 011,即十进制的 3

逻辑运算与条件判断中的数学表达式

在Bash的逻辑运算和条件判断中,数学表达式也经常发挥作用。例如,在 if 语句中,可以使用数学表达式来进行条件判断。

if 语句中的整数比较

if 语句可以使用 -eq(等于)、-ne(不等于)、-gt(大于)、-lt(小于)、-ge(大于等于)和 -le(小于等于)等运算符来比较两个整数。

例如,判断一个数是否大于10:

num=15
if [ $num -gt 10 ]; then
    echo "The number is greater than 10"
else
    echo "The number is less than or equal to 10"
fi

在上述代码中,通过 [ $num -gt 10 ] 来判断变量 num 的值是否大于10,如果是,则输出相应信息。

if 语句中的浮点数比较

由于Bash本身对浮点数比较支持有限,在 if 语句中进行浮点数比较时,通常需要借助 bc。例如,判断一个浮点数是否大于另一个浮点数:

a=3.5
b=2.1
if [ $(echo "$a > $b" | bc) -eq 1 ]; then
    echo "$a is greater than $b"
else
    echo "$a is less than or equal to $b"
fi

这里通过 bc 来比较 ab 的大小,并根据 bc 的输出(1表示真,0表示假)进行条件判断。

结合逻辑运算符

在条件判断中,还可以结合逻辑与(&&)、逻辑或(||)等逻辑运算符。例如,判断一个数是否大于5且小于15:

num=8
if [ $num -gt 5 ] && [ $num -lt 15 ]; then
    echo "The number is between 5 and 15"
else
    echo "The number is not between 5 and 15"
fi

在这个例子中,使用 && 逻辑与运算符连接两个条件,只有当两个条件都满足时,if 语句的条件才为真。

循环中的数学运算

在Bash的循环结构中,数学运算常用于控制循环的次数、更新循环变量等。

for 循环中的数学运算

for 循环常用于已知循环次数的场景。可以通过数学运算来生成循环的范围。

例如,从1到10进行计数:

for ((i = 1; i <= 10; i++)); do
    echo $i
done

在上述代码中,通过 ((i = 1; i <= 10; i++)) 定义了循环变量 i 的初始值为1,循环条件为 i <= 10,每次循环后 i 自增1。

还可以进行更复杂的数学运算。例如,从2开始,每次增加3,直到超过20:

for ((i = 2; i <= 20; i=i + 3)); do
    echo $i
done

这里通过 i=i + 3 来更新循环变量 i 的值,使其每次增加3。

while 循环中的数学运算

while 循环根据条件判断来决定是否继续循环。数学运算可以用于更新循环条件中的变量。

例如,计算1到100的累加和:

sum=0
i=1
while [ $i -le 100 ]; do
    sum=$((sum + i))
    i=$((i + 1))
done
echo "The sum from 1 to 100 is $sum"

在上述代码中,通过 while [ $i -le 100 ] 作为循环条件,在循环体中,通过 sum=$((sum + i)) 计算累加和,通过 i=$((i + 1)) 更新循环变量 i

数学运算在实际脚本中的应用案例

计算文件大小总和

在系统管理中,有时需要计算一个目录下所有文件的大小总和。假设目录为 mydir,可以通过以下脚本实现:

total_size=0
for file in mydir/*; do
    if [ -f $file ]; then
        size=$(stat -c %s $file)
        total_size=$((total_size + size))
    fi
done
echo "The total size of files in mydir is $total_size bytes"

在这个脚本中,通过 for 循环遍历 mydir 目录下的所有文件,使用 stat -c %s 获取文件大小,然后通过数学运算累加到 total_size 变量中。

生成随机数并进行运算

Bash可以生成随机数,并结合数学运算进行各种操作。例如,生成两个1到100之间的随机数,并计算它们的和与平均值:

num1=$((RANDOM % 100 + 1))
num2=$((RANDOM % 100 + 1))
sum=$((num1 + num2))
average=$((sum / 2))
echo "Number 1: $num1"
echo "Number 2: $num2"
echo "Sum: $sum"
echo "Average: $average"

在这个脚本中,通过 RANDOM 变量生成随机数,并通过取模运算将其范围限制在1到100之间,然后进行加法和求平均运算。

计算文件的MD5校验和并进行比较

在文件传输和验证中,常使用MD5校验和。可以计算文件的MD5校验和,并与已知的正确校验和进行比较。假设已知正确的MD5校验和为 correct_md5,文件为 myfile

file_md5=$(md5sum myfile | cut -d ' ' -f 1)
if [ "$file_md5" = "$correct_md5" ]; then
    echo "The file is valid"
else
    echo "The file may be corrupted"
fi

这里通过 md5sum 命令计算文件的MD5校验和,通过字符串比较来判断文件是否有效。虽然这不是严格意义上的数学运算,但体现了在实际脚本中结合各种操作的应用。

通过以上详细介绍,我们全面了解了Bash中的数学运算与表达式求值,包括基本整数运算、浮点数运算、位运算、逻辑运算中的应用、循环中的运用以及实际脚本案例,能够帮助开发者在Bash脚本编程中更好地处理各种数学相关的任务。