1. 變量
1.1 變量的分類
全局變量:所有的用戶都可以使用,保存在 /etc/profile、/etc/bashrc文件中。在開機之后,用戶登陸之前,把前面文件中的變量加載到內存中,等待用戶使用。
本地變量:用戶私有變量,只有本用戶可以使用,保存在用戶家目錄下的 .bash_profile、.bashrc文件中。在用戶登錄成功之后才會加載到內存中。
用戶自定義變量:由用戶自定義,比如腳本中的變量。
1.2 定義變量
變量格式: 變量名=值 (注意:在shell編程中的變量名和等號之間不能有空格)
變量命名規則:
命名只能使用英文字母,數字和下划線,首個字符不能以數字開頭;
中間不能有空格,可以使用下划線(_);
不能使用標點符號;
不能使用bash里的關鍵字(可用help命令查看保留關鍵字);
建議變量名全部用大寫,和命令區分開
定義全局變量 -- export
[root@haha ~]# export UNAME="xiaoming" [root@haha ~]# echo $UNAME xiaoming
這種方式設置的變量是一次性變量,系統重啟后就會丟失。如果希望可以永久使用,可以將需要定義的變量寫入變量文件中即可。
定義永久變量 -- 變量文件
定義全局的永久變量:vim /etc/profile,添加全局變量 export UNAME="xiaoming",重啟配置文件生效 source /etc/profile
定義本地的永久變量:vim ~/.bash_profile,添加本地變量 UNAME="xiaoming",重啟配置文件生效 source ~./bash_profile
定義普通變量 -- 變量賦值
[root@www ~]# UNAME="xiaoming" [root@www ~]# SCHOOL='nanjing' [root@www ~]# AGE=25 [root@www ~]# SCORE=99
預定義變量
$0:腳本名 $*:所有的參數,具體區別見下 $@:所有的參數,具體區別見下 $#:參數個數 $$:當前進程的pid $!:上一個后台進程的pid $0:上一個命令的返回值,0表示執行成功
預定義變量里, $@ 和 $* 是有區別的:
$* 和 $@ 都表示傳遞給函數或腳本的所有參數。
當 $* 和 $@ 不被雙引號" "包圍時,它們之間沒有任何區別,都是將接收到的每個參數看做一份數據,彼此之間以空格來分隔。
但是當它們被雙引號" "包含時,就會有區別了:
"$*"會將所有的參數從整體上看做一份數據,而不是把每個參數都看做一份數據。
"$@"仍然將每個參數都看作一份數據,彼此之間是獨立的。
比如傳遞了5個參數,那么對於"$*"來說,這5個參數會合並到一起形成一份數據,它們之間是無法分割的;而對於"$@"來說,這5個參數是相互獨立的,它們是5份數據。
這種區別在使用函數傳入數組參數時就顯現出來了(詳細參考 https://i-beta.cnblogs.com/posts/edit-done;postId=12832432 ,1.4.2 shell函數傳參)。
預定義變量的使用
#!/bin/bash #ping一個文本中的所有ip if [ $# -eq 0 ]; then
echo "error info: `basename $0` need detail arguments!" exit fi
if [ ! -f $1 ]; then
echo "error info: it mast be a file, not directory!" exit fi
for ip in `cat $1` do
ping -c1 $ip &>/dev/null
if [ $? -eq 0 ]; then
echo "$ip is up"
else
echo "$ip is down"
fi
done
結果如下:
[root@haha shell_0502]# sh ping_txt.sh
error info: ping_txt.sh need detail arguments!
[root@haha shell_0502]#
[root@haha shell_0502]# sh ping_txt.sh ./test.txt
1.3 引用變量
$變量名 或 ${變量名}
1.4 操作變量
讀取變量: echo $變量名 或 echo ${變量名}
[root@haha ~]# UNAME="xiaoming" [root@haha ~]# echo $UNAME xiaoming
查看變量長度:echo ${#變量名}
[root@haha shell_0503]# NAME='alibaba' [root@haha shell_0503]# echo ${#NAME} 7
切片: echo ${變量名: 開始位置: 切片個數}
[root@haha shell_0503]# NAME='alibaba' [root@haha shell_0503]# [root@haha shell_0503]# echo ${NAME:1:2} li
內容替換:echo ${變量名/替換前內容/替換后內容}, 原來的變量不發生變化
[root@haha shell_0503]# name="alibaba" [root@haha shell_0503]# [root@haha shell_0503]# echo ${name} alibaba [root@haha shell_0503]# [root@haha shell_0503]# echo ${name/b/B} aliBaba [root@haha shell_0503]# echo ${name//a/A}
AlibAbA
[root@haha shell_0503]# echo $name
alibaba
變量替代:原來的變量不發生變化
${變量名-新值}:變量沒有被賦值,就會使用新變量重新賦值;如果變量賦值(包括空值),則不會重新賦值
[root@haha shell_0503]# unset var1 [root@haha shell_0503]# echo ${var1-aaa} aaa [root@haha shell_0503]# var2= [root@haha shell_0503]# echo ${var2-bbb} # 還是空值 [root@haha shell_0503]# var3=333 [root@haha shell_0503]# echo ${var3-ccc} 333 [root@haha shell_0503]# var4='' [root@haha shell_0503]# echo ${var4-ddd} # 還是空值 [root@haha shell_0503]#
${變量名:-新值}:變量沒有被賦值(空值也算沒有賦值),就會使用新變量重新賦值;如果變量賦值(不為空),則不會重新賦值
[root@haha shell_0503]# var1= [root@haha shell_0503]# echo ${var:-aaa} aaa [root@haha shell_0503]# var2=222 [root@haha shell_0503]# echo ${var2:-bbb} 222
1.5 取消變量 -- unset
unset取消不了永久變量,在重啟之后又會恢復,除非在變量文件中刪除。
[root@haha ~]# unset UNAME [root@haha ~]# [root@haha ~]# echo $UNAME
2. 數組
2.1 基本數組
基本數組,即數組索引從0開始,不允許用戶自定義索引的數組。
1. 基本數組語法: 數組名稱=(元素1 元素2 元素3 元...)
2. 基本數組查詢: echo ${數組名稱[index]}
3. 基本數組賦值:數組名[index]=元素值
4. 查看所有數組: declare -a
[root@haha ~]# NAME_ARRAY=('a' 'b' 'c' 'd') [root@haha ~]# declare -a
declare -a ARRAY1='([0]="a" [1]="b" [2]="c" [3]="d" [4]="e" [5]="f")' declare -a NAME_ARRAY='([0]="a" [1]="b" [2]="c" [3]="d")' ...
5. 訪問數組元素:
echo ${NAME_ARRAY[0]} # 訪問數組第一個元素 echo ${NAME_ARRAY[@]} 或 echo ${NAME_ARRAY[*]} # 訪問數組中的所有元素, 當用雙引號引起來時("${NAME_ARRAY[@]}" 和 "${NAME_ARRAY[*]}")還是有很大區別的。 echo ${#NAME_ARRAY[@]} # 統計數組的長度 echo ${!NAME_ARRAY[@]} # 獲取數組的元素索引 echo ${NAME_ARRAY[@]:2} # 數組切片,從索引為2到結束的元素 echo ${NAME_ARRAY[@]:1:2} # 從索引為1開始獲取兩個元素
6. for循環遍歷數組 ---- 根據數組的索引遍歷索引對應的值(推薦使用,沒有局限性)
第一種遍歷: 直接遍歷數組的值(有局限性: 當數組的元素有空格時, 遍歷會出錯)
ME_ARRAY=('a' 'b' 'c' 'd') for i in ${ME_ARRAY[@]} do echo $i done
第二種遍歷:根據索引遍歷索引對應的值
ME_ARRAY=('a' 'b' 'c' 'd') for i in ${!ME_ARRAY[@]} do echo ${ME_ARRAY[$i]} done
練習: 把hosts文件組成一個數組
注意: host_array[i++]=$line, 這塊不用事先聲明數組或變量i(默認i=0),不像其他語言那樣嚴格。
#!/bin/bash # 把hosts文件內容組成一個數組,並遍歷輸出 while read line do host_array[i++]=$line done < /etc/hosts
# 如果這塊直接遍歷數組值的話, 由於每一個元素有空格存在,所以每個有空格的元素會被多次遍歷,導致出錯。 for i in ${!host_array[@]} do echo "$i: ${host_array[i]}" done
2.2 關聯數組
關聯索引,即允許用戶自定義數組索引,使用起來更方便高效。
1. 關聯數組語法:
# 聲明一個關聯數組變量 declare -A ASS_ARRAY
ASS_ARRAY=([index1]='元素1' [index2]='元素2' [index3]='元素3' [index]=...)
2. 關聯數組查詢:
[root@haha ~]# declare -A ASS_ARRAY [root@haha ~]#
ASS_ARRAY=([name]='xiaoming' [age]=25 [gender]='male') [root@haha ~]# echo ${ASS_ARRAY[name]} xiaoming [root@haha ~]# echo ${ASS_ARRAY[gender]} male
3. 訪問關聯數組元素的長度、索引、切片等方法同基本數組一樣。
4. 關聯數組賦值(同基本數組):關聯數組[index]=元素
[root@haha ~]# ASS_ARRAY[score]=100 [root@haha ~]# echo ${ASS_ARRAY[score]} 100
2.3 數組的追加
對於基本數組:arr+=(要追加的值)
[root@haha array_gender]# arr1=("a" "b" "c") [root@haha array_gender]# arr1+=("d") [root@haha array_gender]# echo ${arr1[@]} a b c d
對於關聯數組:arr+=([索引]=值)
[root@haha array_gender]# declare -A arr [root@haha array_gender]# arr=([name]='xiaoming' [age]=25 [gender]='male') [root@haha array_gender]# arr+=([location]='shanghai') [root@haha array_gender]# echo ${arr[@]} male xiaoming 25 shanghai
2.4 數組練習
例1: 對info信息進行性別統計,男女各多少人(用關聯數組,把要統計的性別作為索引,把累加的個數作為索引值)
如果直接命令行查看: awk '{print $2}' info.txt |sort | uniq -c
用腳本實現:
xiaofang female
yuanfang male
yingzheng male
xiaoqiao female
diaochan female
lvbu male
mengqi xxx
#!/bin/bash #統計info.txt中男女各多少人 declare -A gender_array while read line do gender=`echo $line | awk '{print $2}'` let gender_array[$gender]++
done < info.txt for i in ${!gender_array[@]} do
echo "$i: ${gender_array[$i]}"
done
3. shell的條件運算
3.1 數學比較運算
注意:這種比較運算只能支持整型,不支持浮點型,可以先將浮點型*10轉成整型然后再判斷。
運算符解釋 -eq 等於 -gt 大於 -lt 小於 -ge 大於或等於 -le 小於或等於 -ne 不等於
3.2 字符串比較運算
運算符解釋,注意字符串一定別忘了使用引號引起來
== 等於 != 不等於 -n 檢查字符串的長度是否大於0 -z 檢查字符串的長度是否為0
3.3 邏輯運算
注意: 邏輯運算符不能用於 [ ] 中,但是可以用在 [[ ]] 里;[ ] 中 -a 表示 and, -o 表示 or
邏輯與運算 && 邏輯或運算 || 邏輯非運算 ! 注意: 邏輯與 或 運算都需要兩個或以上條件,邏輯非運算只能一個條件。 口訣: 邏輯與運算 真真為真 真假為假 假假為假 邏輯或運算 真真為真 真假為真 假假為假 邏輯非運算 非假為真 非真為假
3.4 文件比較與檢查
-d 檢查文件是否存在且為目錄 -e 檢查文件或目錄是否存在 -f 檢查文件是否存在且為文件 -r 檢查文件是否存在且可讀 -s 檢查文件是否存在且不為空 -w 檢查文件是否存在且可寫 -x 檢查文件是否存在且可執行 -O 檢查文件是否存在並且被當前用戶擁有 -G 檢查文件是否存在並且默認組為當前用戶組 file1 -nt file2 檢查file1是否比file2新 file1 -ot file2 檢查file1是否比file2舊 file1 -ef file2 檢查兩個文件的索引位置是否相同,例如文件和該文件的硬鏈接索引位置相同
4. if語法
4.1 if基礎語法
if [ condition ] # condition 值為 true or false,必須和 [ ] 之間有空格, 否則報錯,可以使用 " == " 或 " != "
then
commands
fi # if 代碼塊結束的標志,必須得有,不然報錯
下面兩個腳本中的 $1,$2 在 shell 中稱為位置參數,表示命令行傳入的第1個參數, 第2個參數...第10個位置參數表示 ${10}
如何執行含有參數的腳本? bash judge_num.sh 2 5,即表示執行這個腳本,第1個位置參數為2,第2個位置參數為5
#!/bin/bash #判斷兩個數的大小,if嵌套。$1表示第一個位置參數,$2表示第二個位置參數 if [ $1 -eq $2 ] then
echo "$1 = $2"
else
if [ $1 -gt $2 ] then
echo "$1 > $2"
else
echo "$1 < $2"
fi
fi
#!/bin/bash #判斷兩個數大小,if - elif -else
if [ $1 -eq $2 ] then
echo "$1 = $2"
elif [ $1 -gt $2 ] then
echo "$1 > $2"
else
echo "$1 < $2"
fi
注意:如果是 if 嵌套的話一定記得每個 if 代碼塊都要寫 fi
4.2 if 高級用法
1. 條件語句中使用 (( 條件...)),可以在條件中植入數學表達式,用來進行數學運算。就可以使用傳統的比較運算符 >、<、 >=、<= 等,不用再使用 -eq,-gt,-lt等
注意: if非數學運算的判斷條件盡量使用[ ],(()) 只能用於數學運算,不能用於判斷字符串和字符串是否相等(使用(())判斷出來的結果是相等),字符串和數字是否相等(使用(())判斷出來的結果也是相等)。
#!/bin/bash
# 正確使用 if (( 100%3+1>5 ));then
echo "yes"
else
echo "no"
fi
#!/bin/bash # 錯誤使用,具體原因看下圖紅色注釋說明 read -p "輸入字符>>> " ch #if (( $ch == "a" ));then
if [ $ch == "a" ];then
echo "字母:$ch"
#elif (( $ch == 0 ));then
elif [ $ch == 0 ];then
echo "數字:$ch"
fi
2. 條件語句中使用 [[條件...]],可以放多個條件,也條件中使用正則。
放多個條件:[[ 2 -eq 2 && 5 -lt 9 ]]
使用正則:[[ "abcde" =~ ^[a-z]+$ ]]
#!/bin/bash # 如果 jk jm an ae js aw 中,有 a 開頭的,就輸出
for var in jk jm an ae js aw do
if [[ "$var" =~ a* ]] then
echo "$var"
fi
done
用腳本實現: if判斷磁盤使用率是否超過90%
注意:awk 參數 -F("%"),表示用 "%" 作為分隔符
#!/bin/bash #磁盤使用率腳本 disk_use=`df -h | grep "/$" | awk '{print $(NF-1)}' | awk -F"%" '{print $1}'` if [ $disk_use -ge 90 ] then
echo "`date +"%F %H:%M:%S"` 磁盤使用率:${disk_use}%, 未超過90%"
else
echo "`date +"%F %H:%M:%S"` 磁盤使用率:${disk_use}%, 未超過90%"
fi
以調試模式運行腳本: sh -vx disk_use.sh(可以看到每一步的結果)
5. case多條件分支語句(用法相當於 if...elif...elif...elif...elif...else...fi)
在生產環境中,我們總會遇到一個問題需要根據不同的狀況來執行不同的預案,那么我們要處理這樣的問題就要首先根據可能出現的情況寫出對應預案,根據出現的情況來加載不同的預案。
特點:根據給予的不同條件執行不同的代碼塊
5.1 case語法
case 變量 in
條件1)
執行代碼塊1
;;
條件2)
執行代碼塊2
;;
......
esac
注意:每個代碼塊執行完畢要以;;結尾代表結束,case結尾要以倒過來寫的esac來結束。
#!/bin/bash #case使用語法 web1=192.168.12.60 web2=192.168.12.56 web3=192.168.17.125 cat << EOF 1. web1:$web1 2. web2:$web2 3. web3:$web3 EOF read -p "input the number that you need to collect: " input_ip case "$input_ip" in 1) ssh alice@$web1 ;; 2) ssh alice@$web2 ;; 3) ssh alice@$web3 ;; "") echo "input do not empty!" ;; *) echo "error input!" ;; esac