第八章:操作符和相关主题
8.1. 操作符
赋值
- 变量赋值
-
初始化或改变一个变量的值
- =
-
通用的变量赋值操作符,可以用于数值和字符串的赋值
1 var=27 2 category=minerals # "="字符后面不能加空白字符.
不要把"="赋值操作符和=测试操作符搞混了。
1 # = 用于测试操作符 2 3 if [ "$string1" = "$string2" ] 4 # if [ "X$string1" = "X$string2" ] 会更安全, 5 # 它为了防止其中有一个字符串为空时产生错误信息. 6 # (增加的"X"字符可以互相抵消.) 7 then 8 command 9 fi
计算操作符
- +
-
加
- -
-
减
- *
-
乘
- /
-
除
- **
-
求幂
1 # Bash在版本2.02引入了"**"求幂操作符. 2 3 let "z=5**3" 4 echo "z = $z" # z = 125
- %
-
求模(它返回整数整除一个数后的余数)
bash$ expr 5 % 3 2
5/3 = 1 余 2
该运算符可用于生成特定范围内的数字(参见示例 9-24 和示例 9-27)和格式化程序输出(参见示例 26-15 和示例 A-6)。 它甚至可以用于生成素数(参见示例 A-16)。 模数令人惊讶地经常出现在各种数字配方中。
例子 8-1. 最大公约数
1 #!/bin/bash 2 # gcd.sh: 最大公约数 3 # 用Euclid运算法则 4 5 # 两个整数的"最大公约数" 6 #+ 是能被这两个整数整除的大最整数. 7 8 # Euclid运算法则采用逐次除法. 9 # 每一次都重新赋值, 10 #+ 被除数 <--- 除数 11 #+ 除数 <--- 余数 12 #+ 直到 余数 = 0. 13 #+ 最后被传递的值中:最大公约数 = 被除数. 14 # 15 # 关于Euclid运算法则的讨论有一个出色的讨论, 16 # 访问Jim Loy的网站, http://www.jimloy.com/number/euclids.htm. 17 18 19 # ------------------------------------------------------ 20 # 参数检查 21 ARGS=2 22 E_BADARGS=65 23 24 if [ $# -ne "$ARGS" ] 25 then 26 echo "Usage: `basename $0` first-number second-number" 27 exit $E_BADARGS 28 fi 29 # ------------------------------------------------------ 30 31 32 gcd () 33 { 34 35 dividend=$1 # 随意赋值. 36 divisor=$2 #+ 这里在两个参数赋大的还是小的都没有关系. 37 # 为什么? 38 39 remainder=1 # 如果在循环中使用未初始化的变量, 40 #+ 在循环中第一个传递值会使它返回一个错误信息 41 # 42 43 until [ "$remainder" -eq 0 ] 44 do 45 let "remainder = $dividend % $divisor" 46 dividend=$divisor # 现在用最小的两个数字来重复. 47 divisor=$remainder 48 done # Euclid运算法则 49 50 } # 最后的$dividend变量值就是最大公约数. 51 52 53 gcd $1 $2 54 55 echo; echo "GCD of $1 and $2 = $dividend"; echo 56 57 58 # 练习: 59 # -------- 60 # 检测命令行参数以确保它们是整数, 61 #+ 如果不是整数则给出一个适当的错误信息并退出脚本. 62 63 exit 0
+=
"加-等(plus-equal)" (把原变量值增加一个常量并重新赋值给变量)
let "var += 5"会使变量var值加了5并把值赋给var.
-=
"(减-等)minus-equal" (把原变量值减少一个常量并重新赋值给变量)
*=
"(乘-等)times-equal" (把原变量值乘上一个常量并重新赋值给变量)
let "var *= 4" 使变量var的值乘上4并把值赋给var.
/=
"(除-等)slash-equal" (把原变量值除以一个常量并重新赋值给变量)
%=
"(模-等)mod-equal" (把原变量值除以一个常量整除(译者注:即取模)并重新赋余数的值给变量)
例子 8-2. 使用计算操作符
1 #!/bin/bash 2 # 用10种不同的方法计数到11. 3 4 n=1; echo -n "$n " 5 6 let "n = $n + 1" # let "n = n + 1"也可以. 7 echo -n "$n " 8 9 10 : $((n = $n + 1)) 11 # ":"是需要的, 12 #+ 否则Bash会尝试把"$((n = $n + 1))"作为命令运行. 13 echo -n "$n " 14 15 (( n = n + 1 )) 16 # 上面是更简单的可行的办法. 17 # 多谢David Lombard指出这一点. 18 echo -n "$n " 19 20 n=$(($n + 1)) 21 echo -n "$n " 22 23 : $[ n = $n + 1 ] 24 # ":"是需要的, 25 #+ 否则Bash会尝试把"$[ n = $n + 1 ]"作为命令运行. 26 # 即使"n"被当作字符串来初始化也能工作. 27 echo -n "$n " 28 29 n=$[ $n + 1 ] 30 # 即使"n"被当作字符串来初始化也能工作. 31 #* 应避免这种使用这种结构,因为它是被废弃并不可移植的. 32 # 多谢Stephane Chazelas. 33 echo -n "$n " 34 35 # 现在是C风格的增加操作. 36 # 多谢Frank Wang指出这一点. 37 38 let "n++" # let "++n"也可以. 39 echo -n "$n " 40 41 (( n++ )) # (( ++n )也可以. 42 echo -n "$n " 43 44 : $(( n++ )) # : $(( ++n ))也可以. 45 echo -n "$n " 46 47 : $[ n++ ] # : $[ ++n ]]也可以. 48 echo -n "$n " 49 50 echo 51 52 exit 0
Bash中的整数变量实际上是有符号的长整数(32位),它的范围在-2147483648至2147483647之间。如果有在此范围限制之外的操作将会得到一个错误的结果。
1 a=2147483646 2 echo "a = $a" # a = 2147483646 3 let "a+=1" # 把变量"a"的值自增一. 4 echo "a = $a" # a = 2147483647 5 let "a+=1" # 再自增"a"一次,超过这个限制. 6 echo "a = $a" # a = -2147483648 7 # 错误 (溢出)
到2.05b版本为止,Bash支持64位的整数。
Bash不能处理浮点计算。它会把含有小数点的数当成字符串。
1 a=1.5 2 3 let "b = $a + 1.3" # 错误 4 # t2.sh: let: b = 1.5 + 1.3: syntax error in expression (error token is ".5 + 1.3") 意为表达式错误(错误的符号".5 + 1.3") 5 6 echo "b = $b" # b=1
在脚本中用bc需要浮点计算或数学库函数的支持。
位操作符. 位操作符很少在脚本中使用。他们主要用于操作和测试从端口或sockets中读到的数据。“位运算”更多地用于编译型的语言,比如说C和C++,它们运行起来快地像飞。
位操作符
- <<
-
位左移(每移一位相当乘以2)
- <<=
-
"位左移赋值"
let "var <<= 2" 结果使var的二进制值左移了二位(相当于乘以4)
- >>
-
位右移(每移一位相当除以2)
- >>=
-
"位右移赋值"(和<<=相反)
- &
-
位与
- &=
-
"位于赋值"
- |
-
位或
- |=
-
"位或赋值"
- ~
-
位反
- !
-
位非
- ^
-
位或
- ^=
-
"位或赋值"
逻辑操作符
&&
逻辑与
1 if [ $condition1 ] && [ $condition2 ] 2 # 等同于: if [ $condition1 -a $condition2 ] 3 # 如果condition1和condition2都为真则返回真... 4 5 if [[ $condition1 && $condition2 ]] # Also works. 6 # 注意&&操作不能在[ ... ]结构中使用.
依据上下文,&&也可以在与列表(and list)连接命令中。
||
逻辑或
1 if [ $condition1 ] || [ $condition2 ] 2 # 等同于: if [ $condition1 -o $condition2 ] 3 # 如果condition1和condition2有一个为真则返回真... 4 5 if [[ $condition1 || $condition2 ]] # Also works. 6 # 注意||操作不能在[ ... ]结构中使用.
ash测试由逻辑操作符连接起来的每一个表达式的退出状态。
例子 8-3. 使用&&和||进行混合条件测试
1 #!/bin/bash 2 3 a=24 4 b=47 5 6 if [ "$a" -eq 24 ] && [ "$b" -eq 47 ] 7 then 8 echo "Test #1 succeeds." 9 else 10 echo "Test #1 fails." 11 fi 12 13 # 错误: if [ "$a" -eq 24 && "$b" -eq 47 ] 14 #+ 这会尝试执行' [ "$a" -eq 24 ' 15 #+ 然后会因没找到匹配的']'而失败. 16 # 17 # 注意: if [[ $a -eq 24 && $b -eq 24 ]]也可以. 18 # 双方括号的if-test比 19 #+ 单方括号的结构更灵活. 20 # (第17行和第6行的"&&"有不同的意思.) 21 # 多谢Stephane Chazelas指出这一点. 22 23 24 if [ "$a" -eq 98 ] || [ "$b" -eq 47 ] 25 then 26 echo "Test #2 succeeds." 27 else 28 echo "Test #2 fails." 29 fi 30 31 32 # -a和-o选项提供 33 #+ 混合条件测试另一个选择. 34 # 多谢Patrick Callahan指出这一点. 35 36 37 if [ "$a" -eq 24 -a "$b" -eq 47 ] 38 then 39 echo "Test #3 succeeds." 40 else 41 echo "Test #3 fails." 42 fi 43 44 45 if [ "$a" -eq 98 -o "$b" -eq 47 ] 46 then 47 echo "Test #4 succeeds." 48 else 49 echo "Test #4 fails." 50 fi 51 52 53 a=rhino 54 b=crocodile 55 if [ "$a" = rhino ] && [ "$b" = crocodile ] 56 then 57 echo "Test #5 succeeds." 58 else 59 echo "Test #5 fails." 60 fi 61 62 exit 0
在算术计算的环境中,&&和||操作符也可以使用。
bash$ echo $(( 1 && 2 )) $((3 && 0)) $((4 || 0)) $((0 || 0)) 1 0 1 0
杂合的其他操作符
- ,
-
逗号操作符
逗号操作符连接两个或更多的算术操作。所有的操作都被求值(可能会有副作用),但只返回最后一个操作的结构.
1 let "t1 = ((5 + 3, 7 - 1, 15 - 4))" 2 echo "t1 = $t1" # t1 = 11 3 4 let "t2 = ((a = 9, 15 / 3))" # 初始化"a"并求"t2"的值. 5 echo "t2 = $t2 a = $a" # t2 = 5 a = 9
8.2. 数字常量
除非一个数字有特别的前缀或符号,否则shell脚本把它当成十进制的数。一个前缀为0的数字是八进制数。一个前缀为0x的数字是十六进制数。一个数用内嵌的#来求值则看成BASE#NUMBER(有范围和符号限制)(译者注:BASE#NUMBER即:基数#数值,参考下面的例子)。
例子 8-4. 数字常量的表示法
1 #!/bin/bash 2 # numbers.sh: 不同基数的数字表示法. 3 4 # 十进制数: 它是默认的 5 let "dec = 32" 6 echo "decimal number = $dec" # 32 7 # 这儿没有什么特别的. 8 9 10 # 八进制数: 以'0'(零)为前缀 11 let "oct = 032" 12 echo "octal number = $oct" # 26 13 # 结果表示为十进制. 14 # --------- ------ -- ------- 15 16 # 十六进制: 以'0x'或'0X'为前缀 17 let "hex = 0x32" 18 echo "hexadecimal number = $hex" # 50 19 # 以十进制的形式表示. 20 21 # 其他的进制的表示形式: BASE#NUMBER 22 # BASE值在2和64之间. 23 # NUMBER必须使用在BASE范围内的符号,看下面的示例. 24 25 26 let "bin = 2#111100111001101" 27 echo "binary number = $bin" # 31181 28 29 let "b32 = 32#77" 30 echo "base-32 number = $b32" # 231 31 32 let "b64 = 64#@_" 33 echo "base-64 number = $b64" # 4031 34 # 这个符号只能工作在ASCII码值为2-64的范围限制. 35 # 10个数字+26个小写字母+26个大写字母+ @ + _ 36 37 38 echo 39 40 echo $((36#zz)) $((2#10101010)) $((16#AF16)) $((53#1aA)) 41 # 1295 170 44822 3375 42 43 44 # 重要提示: 45 # -------------- 46 # 使用一个超出给定进制的数字将会引起一个错误信息。 47 #+ gives an error message. 48 49 let "bad_oct = 081" 50 # ((部分的) 错误信息输出: 51 # bad_oct = 081: value too great for base (error token is "081") 52 # 八进制数字只能使用数字0-7. 53 54 exit 0
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律