1. 程式人生 > 實用技巧 >第三章 變數值操作和運算子

第三章 變數值操作和運算子

一、變數值的操作

1)獲取變數值長度

[root@jh /]# x="hello"
[root@jh /]# echo ${#x}
5

# 企業面試題:已知變數msg='hello world!',請統計出變數中包含的字元數量
# 方法一:
[root@jh /]# echo ${#msg}
12
# 方法二:
[root@jh /]# echo $msg | wc -L
12
# 方法三:
[root@jh /]# echo $msg|awk '{print length}'
12
# 方法四:
[root@jh ~]# expr length "$msg" #length是一個函式,注意因為msg的值有空格,所以$msg必
須用引號包含
12

2)切片

語法:${paramter:offset:length}

#示例
[root@jh /]# msg="abcdef"
[root@jh /]# echo ${msg:3} # 從3號索引開始,一直到最後
def
[root@jh /]# echo ${msg:3:2} # 從3號索引開始,往後數2個字元
de
[root@jh /]# echo ${msg::3} # 從0開始,往後數3個字元
abc

3)截斷

# =================》一、砍掉左邊的字元《=================
# 1.1 簡單使用
[root@jh ~]# url="www.sina.com.cn"
[root@jh ~]# echo ${url#www.}
sina.com.cn
# 1.2 結合*=》非貪婪,預設情況下*是非貪婪,儘可能地少“吃”字元
[root@jh ~]# echo ${url#*w}
ww.sina.com.cn
# 1.3 結合*=》貪婪,儘可能地多“吃”字元
[root@jh ~]# echo ${url##*w} # *會盡可能多地吃掉字元,一直匹配到最遠的那個w才停下來
.sina.com.cn
# =================》二、砍掉右邊的字元《=================
# 1.1 簡單使用
[root@jh ~]# url="www.sina.com.cn"
[root@jh ~]# echo ${url%.cn}
www.sina.com
# 1.2 結合*=》非貪婪
[root@jh ~]# echo ${url%.*}
www.sina.com
# 1.3 結合*=》貪婪
[root@jh ~]# echo ${url%%.*}
www
# =================》三、應用示例《=================
[root@jh ~]# hostname
jh.xxx.com
[root@jh ~]# echo $HOSTNAME
jh.xxx.com
[root@jh ~]# echo ${HOSTNAME%.*}
jh.xxx
[root@jh ~]# echo ${HOSTNAME%%.*}
jh

4)內容的替換

[root@jh ~]# url="www.sina.com.cn"
[root@jh ~]# echo ${url/sina/baidu}
www.baidu.com.cn
[root@jh ~]# echo ${url/n/N}
www.siNa.com.cn
[root@jh ~]# echo ${url//n/N} # 貪婪
www.siNa.com.cN

# 應用示例:批量修改檔名稱
[root@jh shell]# touch jh_2020_{01..05}_linux.txt
[root@jh shell]# ls
jh_2020_01_linux.txt jh_2020_02_linux.txt jh_2020_03_linux.txt
jh_2020_04_linux.txt jh_2020_05_linux.txt
[root@jh shell]# for i in `ls *linux.txt`;do mv $i ${i/_linux/};done
[root@jh shell]# ls
jh_2020_01.txt jh_2020_02.txt jh_2020_03.txt jh_2020_04.txt
jh_2020_05.txt

5)變數的替代

語法:
${x:-臨時變數資訊}
${x:=新的變數資訊}
${x:?沒有設定變數提示資訊} 
${x:+有設定變數提示資訊}

#示例
#1、${parameter-word}: 當調取變數沒有定義過, 就返回word字串資訊
[root@jh ~]# unset name
[root@jh ~]# echo ${name}
[root@jh ~]# echo ${name-"jh"} # 沒有定義過變數name,則使用-後的值
jh
[root@jh ~]#
[root@jh ~]# gender= # 定義過變量了,則使用變數的原值,哪怕變數的值為空值
[root@jh ~]# echo ${gender-"male"}
[root@jh ~]#
[root@jh ~]# age=18 
[root@jh ~]# echo ${age-19} # 定義過變量了,則使用變數的原值
18
[root@jh ~]#

#2、${parameter:-word}: 當調取變數資訊值為空時或未定義變數, 就返回word字串資訊
[root@jh ~]# unset x 
[root@jh ~]# unset y
[root@jh ~]# unset z
[root@jh ~]#
[root@jh ~]# echo ${x:-aaa} # 沒有定義過變數x,則使用-後的值
aaa
[root@jh ~]# y=
[root@jh ~]# echo ${y:-aaa} # 定義過變數y,但變數y的值為空值,則使用-後的值
aaa
[root@jh ~]# z=333
[root@jh ~]# echo ${z:-aaa} # 定義過變量了,並且變數有一個非空的原值,則使用變數的原值
333

#3、{parameter:=word}:當調取變數資訊值為空時或未定義,則設定指定字串為新的變數值
[root@jh /]# unset x
[root@jh /]# echo ${x:=123}
123
[root@jh /]# echo $x
123

#4、${parameter:?word}:當調取變數資訊值為空時或未定義,指定為賦值的錯誤提示資訊
[root@jh /]# unset x
[root@jh /]# echo ${x:?該變數沒有定義過}
-bash: x: 該變數沒有定義過

#5、${parameter:+word}:當調取變數資訊值為空時或未定義,不做任何處理,否則word字串將替代變數
值
[root@jh /]# unset x
[root@jh /]# echo ${x:+哈哈哈}
[root@jh /]# x=123
[root@jh /]# echo ${x:+哈哈哈}
哈哈哈

6)let

# (1) 變數的值
[root@jh ~]# j=1
[root@jh ~]# let ++j
[root@jh ~]# echo $j
2

# (2) 表示式的值
[root@jh ~]# unset i
[root@jh ~]# unset j
[root@jh ~]# i=1
[root@jh ~]# j=1
[root@jh ~]# let x=i++ # 先把i賦值給x,然後再++
[root@jh ~]# let y=++j # 先++j,然後再把j的結果賦值給y
[root@jh ~]# echo $i
2
[root@jh ~]# echo $j
3
[root@jh ~]# echo $x
1
[root@jh ~]# echo $y
2

二、單引號與雙引號

" " 弱引用,引號的特殊字元有意義
' ' 強引用,引號內所有特殊字元都取消意義
[root@localhost ~]# school=“oldboy”
[root@localhost ~]# echo "${school} is good"
oldboy is good
[root@localhost ~]# echo '${school} is good'
${school} is good

三、將命令的結果賦值給變數

# ``與$()
` ` 命令替換 等價於 $() 反引號中的shell命令會被先執行
[root@localhost ~]# touch `date +%F`_file1.txt 
[root@localhost ~]# touch $(date +%F)_file2.txt
[root@localhost ~]# disk_free3="df -Ph |grep '/$' |awk '{print $4}'" # 錯誤
[root@localhost ~]# disk_free4=$(df -Ph |grep '/$' |awk '{print $4}') # 正確
[root@localhost ~]# disk_free5=`df -Ph |grep '/$' |awk '{print $4}'` # 正確

四、運算子

1)算數運算子

#算數運算子
+ - * / %

算數運算子需要配合下述操作使用
# 浮點運算
bc 
# 整數運算
expr
$(())
$[]
let

詳解如下
#1、bc是比較常用的linux計算工具了,而且支援浮點運算
[root@localhost ~]# res=`echo 1+1 | bc`
[root@localhost ~]# echo $res
2
[root@localhost ~]# res=`echo 10 % 3 | bc`
[root@localhost ~]# echo $res
1
[root@localhost ~]# res=`echo 1.2+1.3 | bc`
[root@localhost ~]# echo $res
2.5

[root@localhost ~]# res=`echo 5.0+3.0 | bc`
[root@localhost ~]# echo $res
8.0
[root@localhost ~]# res=`echo "scale=2;5.0/3.0" | bc`
[root@localhost ~]# echo $res
1.66
[root@localhost ~]# res=`echo "scale=2;5.0/6.0" | bc`
[root@localhost ~]# echo $res
.83

#2、expr不支援浮點數計算。而且要注意數字與運算子中的空格
[root@localhost ~]# res=`expr 5 / 3` # 不支援浮點計算
[root@localhost ~]# echo $res
1
[root@localhost ~]# res=`expr 1+1` # 注意:要有空格
[root@localhost ~]# echo $res
1+1
[root@localhost ~]# res=`expr 1 + 1`
[root@localhost ~]# echo $res
2

#3、$(()) 同expr,不支援浮點數運算
# 例如:
[root@localhost ~]# echo $((1+1))
2
[root@localhost ~]# echo $((1.0+2.0)) # 不支援浮點運算子
-bash: 1.0+2.0: 語法錯誤: 無效的算術運算子 (錯誤符號是 ".0+2.0")

# 注意:
echo $(($num1+$num2)) # 也可以簡寫為 echo $((num1+num2))
echo $(((5-3)*2)) # 可以巢狀括號 

#4、$[]同expr以及$(()),不支援浮點運算
[root@jh ~]# echo $[$num1+$num2] # 等同於 echo $[num1+num2]
333
[root@jh ~]# echo $[1.3+3.1]
-bash: 1.3+3.1: 語法錯誤: 無效的算術運算子 (錯誤符號是 ".3+3.1")

#5、let 不支援浮點數運算,而且不支援直接輸出,只能賦值
[root@localhost ~]# let res=1+1
[root@localhost ~]# echo $res
2
[root@localhost ~]#
[root@localhost ~]# let res=50/5
[root@localhost ~]# echo $res
10
[root@localhost ~]# let c=1.3*3
-bash: let: c=1.3*3: 語法錯誤: 無效的算術運算子 (錯誤符號是 ".3*3"

#6、強調:整數與非整數之間運算會報錯
[root@jh ~]# expr 1 + a
expr: 非整數引數
[root@jh ~]# expr 1 + 1.3
expr: 非整數引數

2)測試運算子

測試命令:test,詳細可用man test查詢
測試符號:[],注意只有一層中括號,中括號內左右兩側必須要有空格
test與[]效果都一樣,引數也都一樣

1.測試檔案狀態

1. -d 目錄
[root@jh ~]# test -d /etc/ ; echo $?
0
[root@jh ~]# [ -d /etc ];echo $? # 注意[]內左右兩側要有空格
0
ps:下面關於[]用法都與test一樣,不再舉例


2.-s 檔案長度 > 0、非空
[root@jh ~]# touch a.txt
[root@jh ~]# test -s a.txt ;echo $? # 檔案為空則返回假
1
[root@jh ~]# test -s /etc/passwd ;echo $? # 檔案不為空則返回假
0

3.-f 正規檔案
[root@jh ~]# test -f /etc/passwd;echo $?
0

4.-w 可寫
[jh@jh ~]$ touch a.txt
[jh@jh ~]$ chmod a-w a.txt
[jh@jh ~]$ [ -w a.txt ];echo $? # 返回假,注意,如果是root使用者,無論如何都有
寫許可權
1

5.-r 可讀
[jh@jh ~]$ ll a.txt
-r--r--r--. 1 jh jh 292 8月 17 21:48 a.txt
[jh@jh ~]$ test -r a.txt;echo $?
0

6.-x 可執行
[jh@jh ~]$ ll a.txt
-r--r--r--. 1 jh jh 292 8月 17 21:48 a.txt
[jh@jh ~]$ test -x a.txt;echo $?
1

7.-L 符號連線
[root@jh ~]# ll -d /lib64
lrwxrwxrwx. 1 root root 9 7月 14 03:33 /lib64 -> usr/lib64
[root@jh ~]# test -L /lib64;echo $?
0

8.-u 檔案有 suid 位設定
[root@jh ~]# ll /usr/bin/passwd
-rwsr-xr-x. 1 root root 27832 6月 10 2014 /usr/bin/passwd
[root@jh ~]# [ -u `which passwd` ];echo $?
0
[root@jh ~]# [ -u `which ls` ];echo $?
1

2.字串測試

1.== 兩個字串相等
[root@jh ~]# [ "aaa" == "aaa" ];echo $?
0

2.!= 兩個字串不相等
[root@jh ~]# [ "aaa" != "aaa" ];echo $?
1

3.-z 字串長度為零
[root@jh ~]# ip=""
[root@jh ~]# [ -z "$ip" ];echo $? # 注意引號
0
[root@jh ~]# ip='1.1.1.1'
[root@jh ~]# [ -z "$ip" ];echo $?
1

4.-n 字串長度不為零
[root@jh ~]# [ -n "$ip" ];echo $? # 注意加引號
1

3.測試數值

#test與[]也可以測試數值,與 [[]]和(())效果一樣
-eq 等於
-ne 不等於
-gt 大於
-lt 小於
-ge 大於等於
-le 小於等於
-a並且
-o或者

[root@jh ~]# [ 10 -eq 10 ];echo $?
0
[root@jh ~]# [ 10 -eq 10 -a 10 > 3 ];echo $?
0
# 1、示例:
[root@jh ~]# disk_use=$(df -P |grep '/$' |awk '{print $5}' |awk -F%
'{print $1}')
[root@jh ~]# [ $disk_use -gt 10 ] && echo "warn......"
warn......
[root@jh ~]# [ $(id -u) -eq 0 ] && echo "當前是超級使用者" || echo "you不是超級
使用者"
當前是超級使用者

3)關係運算符

#關係運算符號:
< > <= >= == != && ||
上述關係運算符需要配合(())使用(注意(())屬於C語言風格的比較),最終都是用來當if判斷或者while迴圈的條件

[root@jh ~]# x=100
[root@jh ~]# (($x>10))
[root@jh ~]# echo $?
0
[root@jh ~]# (($x < 10));echo $?
1
[root@jh ~]# (($x == 100));echo $?
0
[root@jh ~]# (($x != 100));echo $?
1
[root@jh ~]# (($x != 100)) && (("jh" == "jh"))
[root@jh ~]# echo $?
1
[root@jh ~]# (($x != 100)) || (("jh" == "jh"))
[root@jh ~]# echo $?
0
[root@jh ~]# (($x != 100 && "jh" == "jh"));echo $?
1
[root@jh ~]# (($x != 100 || "jh" == "jh"));echo $?
0

4)賦值運算子

#賦值運算子號
= += *= /= %=

#示例:
1.=
[root@jh ~]# x=10
[root@jh ~]# echo $x
10

2.+=
[root@db04 ~]# age=18
[root@db04 ~]# ((age+=10))
[root@db04 ~]# echo $age
28

3.*=
[root@db04 ~]# age=18
[root@db04 ~]# ((age*=10))
[root@db04 ~]# echo $age
180

4./= 除數取整
[root@db04 ~]# age=18
[root@db04 ~]# ((age/=10))
[root@db04 ~]# echo $age
1

5.%=  取餘
[root@db04 ~]# age=18
[root@db04 ~]# ((age%=9))
[root@db04 ~]# echo $age
0

5)其他的元字元

[[]]與[]基本一樣,不同的是[[]]支援正則匹配,不過要注意的是必須在內層中括號內左右兩側加空格

[root@jh ~]# [[ "$USER" == "root" ]];echo $? # 注意內層[]中包含的內容必須左右兩側
加空格
0
[root@jh ~]# [[ "$USER" == "root" ]];echo $? # 一個等號也行兩個等號也可以額
0

# 此外[[]]內部是可以使用正則的,注意:正則表示式不要加引號
[root@jh ~]# [[ "$USER" =~ "^root$" ]];echo $? # 正則表示式不要加引號
1
[root@jh ~]# [[ "$USER" =~ ^root$ ]];echo $? # 正則表示式不要加引號
0
[root@jh ~]# [[ ! "$USER" =~ t$ ]] && echo 此時不是管理員登入 || echo 是管理員登入
是管理員登入

[root@jh ~]# num1=123
[root@jh ~]# [[ "$num1" =~ ^[0-9]+$ ]];echo $? # 判斷是否是數字
0
[root@jh ~]# [[ "$num1" =~ ^[0-9]+$ ]] && echo "是數字"
是數字
[root@jh ~]# num2=abc123de
[root@jh ~]# [[ "$num2" =~ ^[0-9]+$ ]];echo $?
1
[root@jh ~]# [[ "$num2" =~ ^[0-9]+$ ]] || echo "num2不是數字"
num2不是數字

6)補充浮點數的比較

# 需要注意的是:bc的結果為1代表真,為0代表假
[root@jh ~]# echo "10.3 >= 10.1" | bc
1
[root@jh ~]# echo "10.3 != 10.1" | bc
1
[root@jh ~]# echo "10.3 != 10.3" | bc
0
# 應用
[root@jh ~]# x=10.3
[root@jh ~]# y=10.1
[root@jh ~]# [ `echo "$x >= $y" | bc` -eq 1 ] && echo "$x 大於 $y"
10.3 大於 10.1

五、總結

1.條件測試:
格式1: test 條件表示式
格式2: [ 條件表示式 ]
格式3: (()) 數值比較,運算 C語言
格式4: [[ 條件表示式 ]],支援正則 =~

2.結合$符號
$[] # 整數運算
$(()) # 整數運算
$() # 命令替換

3.其他
() # 子shell中執行

六、練習題

1)算數運算子練習題

#1、指令碼案例: 編寫小指令碼, 可以實現2位數加減乘除運算
[root@jh shell]# cat a.sh
#!/bin/bash
a=$1
b=$2
[ $# -ne 2 ] && {
echo "請輸入兩位數字資訊"
exit
}
echo "a-b=$((a-b))"
echo "a+b=$((a+b))"
echo "a*b=$((a*b))"
echo "a/b=$((a/b))"

# 2、指令碼案例: 編寫判斷輸入引數是否是整數資訊指令碼
#!/bin/bash
#Auther jh 2020-11-18 version 1 des:/scripts/day03/math03.s
h

read -p "請輸入一個數:" input_num
expr $input_num + 0 &>/dev/null && echo $? &>/dev/null # 只有整型加0才不會報錯,浮點數及字元與0詳解都會報錯
if [ $? -eq 0 ];then
    echo "此數為整數"
else
    echo "此數為非整數"
fi

# 3、如何利用指令碼計算1+2+3+4..10總和數值
方法一:
seq -s "+" 10|bc
方法二:
echo {1..10}|tr " " "+"|bc
方法三:
awk 'BEGIN{for (i=1; i<=10; i++) a=a+i; print a}'
方法四:
declare -i i=1
declare -i sum=0
while ((i<=10))
do
  let sum+=i
  let ++i
done
echo $sum

方法五:
for i in {1..10}
do
  let a=$a+$i
done
  echo $a

2)企業面試題

企業面試題1: 傳入兩個數值資訊, 自動讓指令碼顯示數值比較情況
root@jh shell]# cat num.sh
#!/bin/bash
[ $# -ne 2 ] && echo "只能傳入兩個整數" && exit
[[ ! $1 =~ ^[0-9]+$ ]] && echo "引數1必須是整數" && exit
[[ ! $2 =~ ^[0-9]+$ ]] && echo "引數2必須是整數" && exit
[[ $1 -lt $2 ]] && echo "$1 < $2"
[[ $1 -gt $2 ]] && echo "$1 > $2"
[[ $1 -eq $2 ]] && echo "$1 = $2"
[root@jh shell]# chmod +x num.sh
[root@jh shell]# ./num.sh 17 37
17 < 37