shell教程
OutOfMemory.CN技术专栏-> shell-> shell教程-> 第八章:操作符和相关主题

第八章:操作符和相关主题

赋值变量赋值初始化或改变一个变量的值=通用的变量赋值操作符,可以用于数值和字符串的赋值var=27category=minerals#"="字符后面不能加空白字符.不要把"="赋值操作符和=测试操作符搞混了。#=用于测试操作符if["$string1"="$string2"]#if["X$string1"="X$string2"]会更安全,#它为了防止其中有

赋值

变量赋值

初始化或改变一个变量的值

=

通用的变量赋值操作符,可以用于数值和字符串的赋值

var=27
category=minerals  # "="字符后面不能加空白字符.

不要把"="赋值操作符和=测试操作符搞混了。

#    = 用于测试操作符

if [ "$string1" = "$string2" ]
# if [ "X$string1" = "X$string2" ] 会更安全,
# 它为了防止其中有一个字符串为空时产生错误信息.
# (增加的"X"字符可以互相抵消.)
then
   command
fi

计算操作符

+

-

*

/

**

求幂

# Bash在版本2.02引入了"**"求幂操作符.

let "z=5**3"
echo "z = $z"   # z = 125

%

求模(它返回整数整除一个数后的余数)

 bash$ expr 5 % 3
 2
 	      
5/3 = 1 余 2

This operator finds use in, among other things, generating numbers within a specific range (see Example 9-24 and Example 9-27) and formatting program output (see Example 26-15 and Example A-6). It can even be used to generate prime numbers, (see Example A-16). Modulo turns up surprisingly often in various numerical recipes.


例子 8-1. 最大公约数

#!/bin/bash
# gcd.sh: 最大公约数
#         用Euclid运算法则

#  两个整数的"最大公约数"
#+ 是能被这两个整数整除的大最整数.

#  Euclid运算法则采用逐次除法.
#  每一次都重新赋值,
#+ 被除数 <---  除数
#+ 除数  <---  余数
#+ 直到 余数 = 0.
#+ 最后被传递的值中:最大公约数 = 被除数.
#
#  关于Euclid运算法则的讨论有一个出色的讨论,
#  访问Jim Loy的网站, http://www.jimloy.com/number/euclids.htm.


# ------------------------------------------------------
# 参数检查
ARGS=2
E_BADARGS=65

if [ $# -ne "$ARGS" ]
then
  echo "Usage: `basename $0` first-number second-number"
  exit $E_BADARGS
fi
# ------------------------------------------------------


gcd ()
{

  dividend=$1                    #  随意赋值.
  divisor=$2                     #+ 这里在两个参数赋大的还是小的都没有关系.
                                 #  为什么?

  remainder=1                    #  如果在循环中使用未初始化的变量,
                                 #+ 在循环中第一个传递值会使它返回一个错误信息
                                 #

  until [ "$remainder" -eq 0 ]
  do
    let "remainder = $dividend % $divisor"
    dividend=$divisor            # 现在用最小的两个数字来重复.
    divisor=$remainder
  done                           # Euclid运算法则

}                                # 最后的$dividend变量值就是最大公约数.


gcd $1 $2

echo; echo "GCD of $1 and $2 = $dividend"; echo


# 练习:
# --------
#  检测命令行参数以确保它们是整数,
#+ 如果不是整数则给出一个适当的错误信息并退出脚本.

exit 0

+=

"加-等(plus-equal)" (把原变量值增加一个常量并重新赋值给变量)

let "var += 5"会使变量var值加了5并把值赋给var.

-=

"(减-等)minus-equal" (把原变量值减少一个常量并重新赋值给变量)

*=

"(乘-等)times-equal" (把原变量值乘上一个常量并重新赋值给变量)

let "var *= 4" 使变量var的值乘上4并把值赋给var.

/=

"(除-等)slash-equal" (把原变量值除以一个常量并重新赋值给变量)

%=

"(模-等)mod-equal" (把原变量值除以一个常量整除(译者注:即取模)并重新赋余数的值给变量)

计算操作符常常出现在exprlet命令的表达式中.


例子 8-2. 使用计算操作符

#!/bin/bash
# 用10种不同的方法计数到11.

n=1; echo -n "$n "

let "n = $n + 1"   # let "n = n + 1"也可以.
echo -n "$n "


: $((n = $n + 1))
#  ":"是需要的,
#+ 否则Bash会尝试把"$((n = $n + 1))"作为命令运行.
echo -n "$n "

(( n = n + 1 ))
#  上面是更简单的可行的办法.
#  多谢David Lombard指出这一点.
echo -n "$n "

n=$(($n + 1))
echo -n "$n "

: $[ n = $n + 1 ]
#  ":"是需要的,
#+ 否则Bash会尝试把"$[ n = $n + 1 ]"作为命令运行.
#  即使"n"被当作字符串来初始化也能工作.
echo -n "$n "

n=$[ $n + 1 ]
#  即使"n"被当作字符串来初始化也能工作.
#* 应避免这种使用这种结构,因为它是被废弃并不可移植的.
#  多谢Stephane Chazelas.
echo -n "$n "

# 现在是C风格的增加操作.
# 多谢Frank Wang指出这一点.

let "n++"          # let "++n"也可以.
echo -n "$n "

(( n++ ))          # (( ++n )也可以.
echo -n "$n "

: $(( n++ ))       # : $(( ++n ))也可以.
echo -n "$n "

: $[ n++ ]         # : $[ ++n ]]也可以.
echo -n "$n "

echo

exit 0

Bash中的整数变量实际上是有符号的长整数(32位),它的范围在-2147483648至2147483647之间。如果有在此范围限制之外的操作将会得到一个错误的结果。

a=2147483646
echo "a = $a"      # a = 2147483646
let "a+=1"         # 把变量"a"的值自增一.
echo "a = $a"      # a = 2147483647
let "a+=1"         # 再自增"a"一次,超过这个限制.
echo "a = $a"      # a = -2147483648
                   #      错误 (溢出)

到2.05b版本为止,Bash支持64位的整数。

Bash不能处理浮点计算。它会把含有小数点的数当成字符串。

a=1.5

let "b = $a + 1.3"  # 错误
# t2.sh: let: b = 1.5 + 1.3: syntax error in expression (error token is ".5 + 1.3") 意为表达式错误(错误的符号".5 + 1.3")

echo "b = $b"       # b=1
在脚本中用bc需要浮点计算或数学库函数的支持。

位操作符. 位操作符很少在脚本中使用。他们主要用于操作和测试从端口或sockets中读到的数据。“位运算”更多地用于编译型的语言,比如说C和C++,它们运行起来快地像飞。

位操作符

<<

位左移(每移一位相当乘以2)

<<=

"位左移赋值"

let "var <<= 2" 结果使var的二进制值左移了二位(相当于乘以4)

>>

位右移(每移一位相当除以2)

>>=

"位右移赋值"(和<<=相反)

&

位与

&=

"位于赋值"

|

位或

|=

"位或赋值"

~

位反

!

位非

^

位或

^=

"位或赋值"

逻辑操作符

&&

逻辑与

if [ $condition1 ] && [ $condition2 ]
# 等同于:  if [ $condition1 -a $condition2 ]
# 如果condition1和condition2都为真则返回真...

if [[ $condition1 && $condition2 ]]    # Also works.
# 注意&&操作不能在[ ... ]结构中使用.

依据上下文,&&也可以在与列表(and list)连接命令中。

||

逻辑或

if [ $condition1 ] || [ $condition2 ]
# 等同于:  if [ $condition1 -o $condition2 ]
# 如果condition1和condition2有一个为真则返回真...

if [[ $condition1 || $condition2 ]]    # Also works.
# 注意||操作不能在[ ... ]结构中使用.

Bash测试由逻辑操作符连接起来的每一个表达式的退出状态


例子 8-3. 使用&&和||进行混合条件测试

#!/bin/bash

a=24
b=47

if [ "$a" -eq 24 ] && [ "$b" -eq 47 ]
then
  echo "Test #1 succeeds."
else
  echo "Test #1 fails."
fi

# 错误:   if [ "$a" -eq 24 && "$b" -eq 47 ]
#+         这会尝试执行' [ "$a" -eq 24 '
#+         然后会因没找到匹配的']'而失败.
#
#  注意:  if [[ $a -eq 24 && $b -eq 24 ]]也可以.
#  双方括号的if-test比
#+ 单方括号的结构更灵活.
#    (第17行和第6行的"&&"有不同的意思.)
#    多谢Stephane Chazelas指出这一点.


if [ "$a" -eq 98 ] || [ "$b" -eq 47 ]
then
  echo "Test #2 succeeds."
else
  echo "Test #2 fails."
fi


#  -a和-o选项提供
#+ 混合条件测试另一个选择.
#  多谢Patrick Callahan指出这一点.


if [ "$a" -eq 24 -a "$b" -eq 47 ]
then
  echo "Test #3 succeeds."
else
  echo "Test #3 fails."
fi


if [ "$a" -eq 98 -o "$b" -eq 47 ]
then
  echo "Test #4 succeeds."
else
  echo "Test #4 fails."
fi


a=rhino
b=crocodile
if [ "$a" = rhino ] && [ "$b" = crocodile ]
then
  echo "Test #5 succeeds."
else
  echo "Test #5 fails."
fi

exit 0

在算术计算的环境中,&&和||操作符也可以使用。

 bash$ echo $(( 1 && 2 )) $((3 && 0)) $((4 || 0)) $((0 || 0))
 1 0 1 0
 	      

杂合的其他操作符

,

逗号操作符

逗号操作符连接两个或更多的算术操作。所有的操作都被求值(可能会有副作用),但只返回最后一个操作的结构.

let "t1 = ((5 + 3, 7 - 1, 15 - 4))"
echo "t1 = $t1"               # t1 = 11

let "t2 = ((a = 9, 15 / 3))"  # 初始化"a"并求"t2"的值.
echo "t2 = $t2    a = $a"     # t2 = 5    a = 9

逗号操作符主要用在for 循环里. 参考例子 10-12.

© 内存溢出 OutOfMemory.CN