天天看點

20天入門Shell之Day7 算術運算

作者:Linux後端民工MrWu

7.1 算術表達式

((...))文法可以進行整數的算術運算。

$ ((foo = 5 + 5))
$ echo $foo
10           

((...))會自動忽略内部的空格,是以下面的寫法都正确,得到同樣的結果。

$ ((2+2))
$ (( 2+2 ))
$ (( 2 + 2 ))           

這個文法不傳回值,指令執行的結果根據算術運算的結果而定。隻要算術結果不是0,指令就算執行成功。

$ (( 3 + 2 ))
$ echo $?
0           

上面例子中,3 + 2的結果是5,指令就算執行成功,環境變量$?為0。

如果算術結果為0,指令就算執行失敗。

$ (( 3 - 3 ))
$ echo $?
1           

上面例子中,3 - 3的結果是0,環境變量$?為1,表示指令執行失敗。

如果要讀取算術運算的結果,需要在((...))前面加上美元符号$((...)),使其變成算術表達式,傳回算術運算的值。

$ echo $((2 + 2))
4           

((...))文法支援的算術運算符如下。

+:加法
-:減法
*:乘法
/:除法(整除)
%:餘數
**:指數
++:自增運算(字首或字尾)
--:自減運算(字首或字尾)           

注意,除法運算符的傳回結果總是整數,比如5除以2,得到的結果是2,而不是2.5。

$ echo $((5 / 2))
2           

++和--這兩個運算符有字首和字尾的差別。作為字首是先運算後傳回值,作為字尾是先傳回值後運算。

$ i=0
$ echo $i
0
$ echo $((i++))
0
$ echo $i
1
$ echo $((++i))
2
$ echo $i
2           

上面例子中,++作為字尾是先傳回值,執行echo指令,再進行自增運算;作為字首則是先進行自增運算,再傳回值執行echo指令。

$((...))内部可以用圓括号改變運算順序。
$ echo $(( (2 + 3) * 4 ))
20           

上面例子中,内部的圓括号讓加法先于乘法執行。

$((...))結構可以嵌套。
$ echo $(((5**2) * 3))
75
# 等同于
$ echo $(($((5**2)) * 3))
75           

這個文法隻能計算整數,否則會報錯。

# 報錯
$ echo $((1.5 + 1))           

bash: 文法錯誤

$((...))的圓括号之中,不需要在變量名之前加上$,不過加上也不報錯。
$ number=2
$ echo $(($number + 1))
3           

上面例子中,變量number前面有沒有美元符号,結果都是一樣的。

如果在$((...))裡面使用字元串,Bash 會認為那是一個變量名。如果不存在同名變量,Bash 就會将其作為空值,是以不會報錯。

$ echo $(( "hello" + 2))
2
$ echo $(( "hello" * 2))
0           

上面例子中,"hello"會被當作變量名,傳回空值,而$((...))會将空值當作0,是以乘法的運算結果就是0。同理,如果$((...))裡面使用不存在的變量,也會當作0處理。

如果一個變量的值為字元串,跟上面的處理邏輯是一樣的。即該字元串如果不對應已存在的變量,在$((...))裡面會被當作空值。

$ foo=hello
$ echo $(( foo + 2))
2           

上面例子中,變量foo的值是hello,而hello也會被看作變量名。這使得有可能寫出動态替換的代碼。

$ foo=hello
$ hello=3
$ echo $(( foo + 2 ))
5           

上面代碼中,foo + 2取決于變量hello的值。

最後,$[...]是以前的文法,也可以做整數運算,不建議使用。

$ echo $[2+2]
4           

7.2 數值的進制

Bash 的數值預設都是十進制,但是在算術表達式中,也可以使用其他進制。

number:沒有任何特殊表示法的數字是十進制數(以10為底)。
0number:八進制數。
0xnumber:十六進制數。
base#number:base進制的數。           

下面是一些例子。

$ echo $((0xff))
255
$ echo $((2#11111111))
255           

上面例子中,0xff是十六進制數,2#11111111是二進制數。

7.3 位運算

$((...))支援以下的二進制位運算符。

<<:位左移運算,把一個數字的所有位向左移動指定的位。
>>:位右移運算,把一個數字的所有位向右移動指定的位。
&:位的“與”運算,對兩個數字的所有位執行一個AND操作。
|:位的“或”運算,對兩個數字的所有位執行一個OR操作。
~:位的“否”運算,對一個數字的所有位取反。
^:位的異或運算(exclusive or),對兩個數字的所有位執行一個異或操作。           

下面是右移運算符>>的例子。

$ echo $((16>>2))
4           

下面是左移運算符<<的例子。

$ echo $((16<<2))
64           

下面是17(二進制10001)和3(二進制11)的各種二進制運算的結果。

$ echo $((17&3))
1
$ echo $((17|3))
19
$ echo $((17^3))
18           

7.4 邏輯運算

$((...))支援以下的邏輯運算符。

<:小于
>:大于
<=:小于或相等
>=:大于或相等
==:相等
!=:不相等
&&:邏輯與
||:邏輯或
!:邏輯否           

expr1?expr2:expr3:三元條件運算符。若表達式expr1的計算結果為非零值(算術真),則執行表達式expr2,否則執行表達式expr3。

如果邏輯表達式為真,傳回1,否則傳回0。

$ echo $((3 > 2))
1
$ echo $(( (3 > 2) || (4 <= 1) ))
1           

三元運算符執行一個單獨的邏輯測試。它用起來類似于if/then/else語句。

$ a=0
$ echo $((a<1 ? 1 : 0))
1
$ echo $((a>1 ? 1 : 0))
0           

上面例子中,第一個表達式為真時,傳回第二個表達式的值,否則傳回第三個表達式的值。

7.5 指派運算

算術表達式$((...))可以執行指派運算。

$ echo $((a=1))

1

$ echo $a

1

上面例子中,a=1對變量a進行指派。這個式子本身也是一個表達式,傳回值就是等号右邊的值。

$((...))支援的指派運算符,有以下這些。

parameter = value:簡單指派。
parameter += value:等價于parameter = parameter + value。
parameter -= value:等價于parameter = parameter – value。
parameter *= value:等價于parameter = parameter * value。
parameter /= value:等價于parameter = parameter / value。
parameter %= value:等價于parameter = parameter % value。
parameter <<= value:等價于parameter = parameter << value。
parameter >>= value:等價于parameter = parameter >> value。
parameter &= value:等價于parameter = parameter & value。
parameter |= value:等價于parameter = parameter | value。
parameter ^= value:等價于parameter = parameter ^ value。           

下面是一個例子。

$ foo=5
$ echo $((foo*=2))
10           

如果在表達式内部指派,可以放在圓括号中,否則會報錯。

$ echo $(( a<1 ? (a+=1) : (a-=1) ))           

7.6 求值運算

逗号,在$((...))内部是求值運算符,執行前後兩個表達式,并傳回後一個表達式的值。

$ echo $((foo = 1 + 2, 3 * 4))
12
$ echo $foo
3           

上面例子中,逗号前後兩個表達式都會執行,然後傳回後一個表達式的值12。

7.7 expr指令

expr指令支援算術運算,可以不使用((...))文法。

$ expr 3 + 2
5           

expr指令支援變量替換。

$ foo=3
$ expr $foo + 2
5           

expr指令也不支援非整數參數。

$ expr 3.5 + 2
expr: 非整數參數           

上面例子中,如果有非整數的運算,expr指令就報錯了。

7.8 let指令

let指令用于将算術運算的結果,賦予一個變量。

$ let x=2+3
$ echo $x
5           

上面例子中,變量x等于2+3的運算結果。

注意,x=2+3這個式子裡面不能有空格,否則會報錯。let指令的詳細用法參見《變量》一章。