shell基礎(四)
一:while迴圈
while <條件表示式>#此處可以是(())、[]、[[]]和前面條件表示式判斷一樣 do 指令.. done while迴圈主要是 1.重複執行一組命令,常常用於守護程序或無限迴圈的程式(要加sleep和usleep控制頻率)。 2.適用於頻率小於1分鐘的迴圈處理,其他的都可以用crond功能代替 注: sleep 1# 休息一秒 usleep 1000000# 休息1000000微秒,即休息一秒
例一:
#!/bin/bash while [ 1 ]#[ 1 ]和true 一樣,只是前面2邊需要有空格 do uptime >>/tmp/uptime.log sleep 2 done #uptime用於檢視系統負載,具體可參見http://os.51cto.com/art/201312/420840.htm
例二:計算1+2...100的和
方式一: #!/bib/sh SUM=0 I=1 while [ $I -le 100 ]#[]中需要變數取值符號"$",而且大於小於只能用"-le" do ((SUM+=I)) let I++ done echo $SUM ------------------------------------------------------------------------------------------------- 方式二: #!/bib/sh SUM=0 I=1 while ((I<=100)) #(())中可以不要變數取值符號,大於小於是可以直接用 do ((SUM+=I)) ((I++)) done echo $SUM ------------------------------------------------------------------------------------------------- 方式三: #!/bib/sh SUM=0 I=100 ((SUM=I*(I+1)/2)) echo $SUM
實戰一
手機充值10元,每發一次簡訊花費1角5分,當餘額低於1角5分時就不能在發簡訊,並且提示"餘額不足,請充值",用while實現
簡單版一 #!/bin/bash sum=1000 fee=15 while [ $sum -gt $fee ] do echo "send message successfully" ((sum-=fee)) echo "left $sum" done echo "餘額不足,請充值" -------------------------------------------------------------------------------------------- 專業版二: #!/bin/bash export LANG="zh_CN.UTF-8" #定義中文字元防止亂碼 sum=15#總錢數 msg_fee=15#每條簡訊的錢數 function menu(){ cat <<END 當前餘額${sum},每條簡訊需要${msg_fee} ============== 1.充值餘額 2.傳送簡訊 3.退出 ============== END } #充錢 function recharge(){ read -p "請輸入充值金額:" money expr $money + 1 &>/dev/null if [ $? -ne 0 ];then echo "must be int" else ((sum+=money)) echo "您的餘額為$sum" fi } #發信息 function sendInfo(){ if ((sum < nsg_fee));then#先判斷錢夠不夠 echo "您的餘額不足,請充值" else while true do read -p "請輸入內容(不要帶空格):" message echo "$message 已傳送" ((sum-=msg_fee)) return 0#此處傳送完成後,要去選單選擇,而不是一直髮簡訊 if [ $sum -lt $msg_fee ];then printf "溫馨提示:您的餘額不足" return 1 fi done fi } function main(){ while true#一直顯示選單,若想退出數字"3",即可退出 do menu read -p "請輸入一個數字:" NUM case "$NUM" in 1) recharge ;; 2) sendInfo ;; 3) exit 0 ;; *) echo "must be {1|2|3}" esac done } main
效果如圖所示:
[centos@mycentos iphone_fee]$ sh 2.sh 當前餘額15,每條簡訊需要15 ============== 1.充值餘額 2.傳送簡訊 3.退出 ============== 請輸入一個數字:1 請輸入充值金額:100 您的餘額為115 當前餘額115,每條簡訊需要15 ============== 1.充值餘額 2.傳送簡訊 3.退出 ============== 請輸入一個數字:2 請輸入內容(不要帶空格):hello hello 已傳送 當前餘額100,每條簡訊需要15 ============== 1.充值餘額 2.傳送簡訊 3.退出 ============== 請輸入一個數字:3 [centos@mycentos iphone_fee]$ 注:case常用於服務啟動指令碼中,常用cat的here文件的方式列印選單
實戰二
用while守護程序的方式去監控網站,每10秒確定一次是否正常,若不正常,就發郵箱通知
預備知識:
curl -I www.qq.com: 顯示響應頭部資訊 curl -o 1.txt www.qq.com : 將網頁下載到1.txt檔案中 curl命令解釋: curl -o /dev/null -s -w %{http_code} http://zys.8800.org/ -o 引數,是把下載的所有內容都重定向到/dev/null,-s命令,是遮蔽了curl本身的輸出,而-w引數,是根據我們自己的需要,自定義了curl的輸出格式。 用上面的命令採集頁面的狀態碼,返回200表示頁面正常,其他返回碼則是異常。
程式碼如下: #!/bin/bash if [ $# -ne 1 ];then echo $"usage:$0 url" exit 1 fi while true#守護程序 do #取回頁面的狀態碼 if [ $( curl -o /dev/null--connect-timeout 3 -s -w "%{http_code}" $1 | egrep -w "200|301|302" | wc -l) -ne 1 ];then echo "$1 is error"|mail -s "$1 is error" [email protected] else echo "$1 is ok" fi sleep 10 done
知識點補充:
1、當執行指令碼時,若突然中斷,優肯會導致資料的丟失,則需要防止中斷的方式
1.使用"sh shell.sh &"命令 ,即使用&在後臺執行 2.使用"nohup shell.sh &"命令,即使用nohup 加&在後臺執行指令碼 3.利用screen保持會話,然後再執行指令碼,即使用screen保持當前會話 後臺執行的知識: sh shell.sh &指令碼shell.sh 放在後臺執行 Ctrl+c停止執行當前指令碼或任務 Ctrl+z暫停執行當前指令碼 jobs檢視當前執行的指令碼或任務 bg將當前指令碼或任務放到後臺執行 kill關閉當前指令碼任務,即以"kill %號碼"的形式關閉程序,任務號通過jobs獲得 fg將當前的指令碼或任務放到前臺執行,若有多工,則"fg 號碼"調出相應的任務編號
效果如圖:
[centos@mycentos shell]$ sh 2.sh &#後臺執行 [1] 2988 [centos@mycentos shell]$ jobs#檢視任務數 [1]+Runningsh 2.sh & [centos@mycentos shell]$ kill %1#關閉程序為1的程序 [centos@mycentos shell]$ jobs [1]+Terminatedsh 2.sh #程式已經被關閉
2.while按行讀取檔案的幾種方式
1). exec <FILE sum=0 while read line do cmd done --------------------------------------------------------------------------------------------------- 2). cat file | while read line do cmd done ------------------------------------------------------------------------------------------------- 3).while read line do cmd done<file
例如:開發一個shell指令碼實現cat讀檔案的基本功能
#!/bin/bash while read line do echo $line done<$1
二:for迴圈
for 變數名 in 變數取值列表 do 指令 done 注:讀取取值列表時預設以空格分割 C語言版迴圈 for ((exp1;exp2;exp3)) do 指令。。。 done 例如: for ((i=0;i<=3;i++)) do echo $i done 注:"in變數取值列表" 可以省略,省略時相當於in "$@",即for i 就等於 for i in "$@"
例一:列印5、4、3、2、1這五個數字
方式一: #!/bin/bash #直接列出變數列表,列印5、4、3、2、1 以空格為分隔符 for NUM in 5 4 3 2 1 do echo $NUM done ------------------------------------------------------------------------------------------------- 方式二: #!/bin/sh #用{}號實現 for NUM in {5..1} do echo $NUM done ------------------------------------------------------------------------------------------------- 方式三: #!/bin/bash #5是起始數字 -1是步長,即每次減一 1是結束數字 for NUM in $(seq 5 -1 1) do echo $NUM done
實戰三:批量更改檔名,將目錄下以".txt"結尾的全部變成".gif"
思路:先處理一個,再批量處理
[centos@mycentos test]$ ll
total 0
-rw-rw-r--. 1 centos centos 0 Nov 13 13:58 1.txt
-rw-rw-r--. 1 centos centos 0 Nov 13 13:58 2.txt
-rw-rw-r--. 1 centos centos 0 Nov 13 13:58 3.txt
-rw-rw-r--. 1 centos centos 0 Nov 13 13:58 4.txt
-rw-rw-r--. 1 centos centos 0 Nov 13 13:58 5.txt
1.先處理一個: [centos@mycentos test]$ file=1.txt [centos@mycentos test]$ echo $file 1.txt [centos@mycentos test]$ echo $file | cut -d '.' -f1 1 [centos@mycentos test]$ echo $(echo $file | cut -d '.' -f1).gif#得到要變成的樣子 1.gif [centos@mycentos test]$ mv $file $(echo $file | cut -d '.' -f1).gif [centos@mycentos test]$ ll total 0 -rw-rw-r--. 1 centos centos 0 Nov 13 13:58 1.gif -rw-rw-r--. 1 centos centos 0 Nov 13 13:58 2.txt -rw-rw-r--. 1 centos centos 0 Nov 13 13:58 3.txt -rw-rw-r--. 1 centos centos 0 Nov 13 13:58 4.txt -rw-rw-r--. 1 centos centos 0 Nov 13 13:58 5.txt
2.指令碼批量處理: #!/bin/bash for file in $(ls | grep "txt$") do mv ${file} $(echo $file | cut -d . -f1).gif done 結果: total 0 -rw-rw-r--. 1 centos centos 0 Nov 13 13:58 1.gif -rw-rw-r--. 1 centos centos 0 Nov 13 13:58 2.gif -rw-rw-r--. 1 centos centos 0 Nov 13 13:58 3.gif -rw-rw-r--. 1 centos centos 0 Nov 13 13:58 4.gif -rw-rw-r--. 1 centos centos 0 Nov 13 13:58 5.gif
實戰四:
在linux下批量修改檔名,將下列檔名中"_finished"去掉
-rw-rw-r--. 1 centos centos 0 Nov 13 14:31 sku_102999_1_finished.jpg
-rw-rw-r--. 1 centos centos 0 Nov 13 14:31 sku_102999_2_finished.jpg
-rw-rw-r--. 1 centos centos 0 Nov 13 14:31 sku_102999_3_finished.jpg
-rw-rw-r--. 1 centos centos 0 Nov 13 14:31 sku_102999_4_finished.jpg
-rw-rw-r--. 1 centos centos 0 Nov 13 14:31 sku_102999_5_finished.jpg
方法一: 先處理一個後批量處理 #!/bin/sh for list in $(ls *.jpg) do mv $list$(echo $list |sed 's/_finished//g') done --------------------------------------------------------------------------------------------- 方法二:(awk) [centos@mycentos old]$ ls| awk -F '.' '{print $0,$1,$2}' #$0為本身 ch.sh ch sh sku_102999_1.jpg sku_102999_1 jpg sku_102999_2.jpg sku_102999_2 jpg sku_102999_3.jpg sku_102999_3 jpg sku_102999_4.jpg sku_102999_4 jpg sku_102999_5.jpg sku_102999_5 jpg [centos@mycentos old]$ ls| awk -F '.' '{print $0,$1"_finished."$2}' #進行拼接 ch.sh ch_finished.sh sku_102999_1.jpg sku_102999_1_finished.jpg sku_102999_2.jpg sku_102999_2_finished.jpg sku_102999_3.jpg sku_102999_3_finished.jpg sku_102999_4.jpg sku_102999_4_finished.jpg sku_102999_5.jpg sku_102999_5_finished.jpg [centos@mycentos old]$ ls| awk -F '.' '{print "mv", $0,$1"_finished."$2}' mv ch.sh ch_finished.sh mv sku_102999_1.jpg sku_102999_1_finished.jpg mv sku_102999_2.jpg sku_102999_2_finished.jpg mv sku_102999_3.jpg sku_102999_3_finished.jpg mv sku_102999_4.jpg sku_102999_4_finished.jpg mv sku_102999_5.jpg sku_102999_5_finished.jpg [centos@mycentos old]$ ls| awk -F '.' '{print "mv", $0,$1"_finished."$2}' | bash #將字元交給bash處理 [centos@mycentos old]$ ll total 4 -rw-rw-r--. 1 centos centos 93 Nov 13 14:37 ch_finished.sh -rw-rw-r--. 1 centos centos0 Nov 13 14:31 sku_102999_1_finished.jpg -rw-rw-r--. 1 centos centos0 Nov 13 14:31 sku_102999_2_finished.jpg -rw-rw-r--. 1 centos centos0 Nov 13 14:31 sku_102999_3_finished.jpg -rw-rw-r--. 1 centos centos0 Nov 13 14:31 sku_102999_4_finished.jpg -rw-rw-r--. 1 centos centos0 Nov 13 14:31 sku_102999_5_finished.jpg ------------------------------------------------------------------------------------------------- 方法三: [centos@mycentos old]$ rename "_finished" "" *jpg#不要忘了"" ,空格代表去除 [centos@mycentos old]$ ll total 4 -rw-rw-r--. 1 centos centos 93 Nov 13 14:37 ch_finished.sh -rw-rw-r--. 1 centos centos0 Nov 13 14:31 sku_102999_1.jpg -rw-rw-r--. 1 centos centos0 Nov 13 14:31 sku_102999_2.jpg -rw-rw-r--. 1 centos centos0 Nov 13 14:31 sku_102999_3.jpg -rw-rw-r--. 1 centos centos0 Nov 13 14:31 sku_102999_4.jpg -rw-rw-r--. 1 centos centos0 Nov 13 14:31 sku_102999_5.jpg
rename 例子補充:
批量去掉檔名中的"bd"
[centos@mycentos db]$ ll total 0 -rw-rw-r--. 1 centos centos 0 Nov 13 14:51 bd02.html -rw-rw-r--. 1 centos centos 0 Nov 13 14:51 bd03.html -rw-rw-r--. 1 centos centos 0 Nov 13 14:51 bd04.html -rw-rw-r--. 1 centos centos 0 Nov 13 14:51 bd05.html rename方式: [centos@mycentos db]$ rename "bd" "" *.html [centos@mycentos db]$ ll total 0 -rw-rw-r--. 1 centos centos 0 Nov 13 14:51 02.html -rw-rw-r--. 1 centos centos 0 Nov 13 14:51 03.html -rw-rw-r--. 1 centos centos 0 Nov 13 14:51 04.html -rw-rw-r--. 1 centos centos 0 Nov 13 14:51 05.html 注: 先去掉一個,後批量去除也可
實戰五:通過指令碼實現僅僅sshd,network,crond,sysstat,rsyslog服務在開機時自啟動,chkconfig --list是檢視所有服務開機時的情況。
方式一: 先統一關閉,後開啟特定的 for old in $(chkconfig --list | grep "3:on" | awk '{print $1}' ) do chkconfig --level 3 $old off done for old in crond network rsyslog sysstat sshd do chkconfig --level 3 $old on done ------------------------------------------------------------------------------------------------- 方式二: 因為預設情況下開機需要保留的服務都是開啟,所以將其他的關閉就好 for old in $(chkconfig --list | grep "3:on" |awk '{print $1}' egrep -v "crond|network|rsyslog|sshd|sysstat") do chkconfig --level 3 $old off done ------------------------------------------------------------------------------------------------- 方式三: awk的拼接 chkconfig --list | egrep -v " sshd|crond|rsyslog|sysstat|network" |awk '{print "chkconfig", $1, "off"}'|bash 效果如圖: [root@mycentos ~]# chkconfig --list | egrep -v " sshd|crond|rsyslog|sysstat|network" |awk '{print "chkconfig", $1, "off"}'#awk中的","變成顯示後的空格進行分割 chkconfig wdaemon off chkconfig winbind off chkconfig wpa_supplicant off chkconfig xinetd off chkconfig ypbind off ..... chkconfig --list | egrep -v " sshd|crond|rsyslog|sysstat|network" |awk '{print "chkconfig", $1, "off"}'| bash #將上面的當做命令傳輸到bash執行 可以簡化成: chkconfig | egrep -v " sshd|crond|rsyslog|sysstat|network" |awk '{print "chkconfig", $1, "off"}'| bash
實戰六:列印九九乘法表
#!/bin/sh COLOR='\E[47;30m' RES='\e[0m' for line in $(seq 9) do for col in $(seq ${line}) do if (((line*col)>9));then#此處用來控制輸出格式,當是大於9時後面是一個空格,小於等於9時就是2個空格 echo -en "${COLOR}${col}*${line}=$((line*col))${RES} " #一個空格 -e用來顯示文字,-n為了不換行 else echo -en "${COLOR}${col}*${line}=$((line*col))${RES}" #2個空格 fi done echo ' '#一行完成後換行 done
效果如圖:

未標題-1.png
實戰七:
批量建立10個系統賬號(oldboy01-oldboy10),並且設定密碼(密碼是隨機數,要求是字元和數字的混合)
思路:
1.建立賬號
seq -w 10:表示 01-10 :格式對齊
或者
{01..10}
2.建立賬戶,設定無互動密碼
useradd oldboy
echo 111111 | passwd --stdin oldboy
3.隨機八位密碼(字母數字混合)
小插曲: 生成隨機數的方式 1.利用RANDOM(範圍是0-32767)隨機生成數字,然後利用md5進行加密,取5到12為字元 [root@mycentos ~]# echo $RANDOM |md5sum | cut -c 5-12 1684ebbf 若只有RANDOM,則安全性不高,$RANDOM前面加上自己定製的祕鑰,安全就會增加 echo "oldboy$RANDOM" 此時的就無法破解 2.利用OpenSSL生成隨機數 [root@mycentos shell]# openssl rand -base64 8 FAQco/00NL0= [root@mycentos shell]# openssl rand -base64 80 s0KqTLEA6fIueDcsJkPaqzd3owXNVUcN+d+9FyiMn1sXYShjmsDgbzrndvrn9o8i ZbjkSZzahr1ZMMbLcqJ/DvCfMAhEcZyG/hIvKMzkr8c= 3.利用/dev/urandom ,這裡記錄系統此時狀態,轉換成數字 [root@mycentos shell]# head /dev/urandom | cksum 434988922 2930 [root@mycentos shell]# head /dev/urandom | cksum 3283962471 2161 4.通過日期生成隨機數; [root@mycentos shell]# date +%s%N 1542160944683910406 以上的隨機數長短不一,通過md5sum進行統一格式 1.echo "test$RANDOM" |md5sum|cut -c 2-9 2.openssl rand -base64 80 | md5sum|cut -c 2-9 3.date +%s%N |md5sum|cut -c 2-9 4.head /dev/urandom | md5sum |cut -c 2-9
方式一: #!/bin/bash user="oldboy" passfile="/tmp/user1.log" for num in $(seq -w 10) do useradd $user$num#建立使用者 pass="$(echo "test$RANDOM" |md5sum|cut -c 3-11)"#先用祕鑰加密後取得密碼,用變數儲存 echo "$pass"|passwd --stdin $user$num &>/dev/null#將密碼賦值給使用者 echo -e "user:$user$num\tpasswd:$pass">>$passfile#將使用者資訊放到固定檔案中,-e引數處理特殊字元,此處將"\t"處理成tab效果,而不 是直接輸出"\t" done echo "------------------------------------------------------" cat $passfile 特別注意:RANDOM是一個隨機數,每次都不一樣,因此如果需要用到2次,就一定要先用變數儲存 ------------------------------------------------------------------------------------------------ 方式二: 用chpasswd批量建立密碼 批量建立密碼:用命令chpasswd [root@mycentos shell]# useradd old [root@mycentos shell]# echo "old:123456"|chpasswd [root@mycentos shell]# su - oldgirl#root切換成普通使用者不需要密碼 [oldgirl@mycentos ~]$ su - old Password: [old@mycentos ~]$ whoami old 給多個使用者設定密碼: chpasswd < 密碼檔案 其中密碼檔案格式: 使用者名稱1:口令1 使用者名稱2:口令2 前提:使用者必須存在 #!/bin/bash . /etc/init.d/functions user="xioaming" passfile="/tmp/user.log" for num in $(seq -w 10)#批量建立使用者和密碼 do pass="$(echo "test$RANDOM" |md5sum|cut -c 3-11)" useradd ${user}${num} &>/dev/null && \ echo -e "${user}${num}:$pass" >> $passfile if [ $? -eq 0 ];then action "${user}${num} is ok" /bin/true else action "${user}${num} is fail" /bin/false fi done echo "-------------------------------------------------" chpasswd < $passfile#chpasswdd 用於批量給使用者設定密碼,此處將上面批量設定的使用者和密碼一一對應 cat$passfile && > $passfile
實戰八:
列印選擇選單,按照選項意見按照不同的web服務
[root@mycentos select]# sh ofollow,noindex">menu.sh
1.[install lamp]
2.[install lanp]
3.exit
input the num you want:工作中所用的lamp
要求:
1.當用戶輸入1時,輸出"start installing lamp"提示,然後執行/server/scripts/lamp.sh輸出"lamp is installed",並且退出指令碼,此為工作中所用的lamp一鍵安裝指令碼
2.當用戶輸入2時,輸出"start installing lnmp" 提示,然後執行/server/scripts/lnmp.sh輸出"lnmp is installed",並退出指令碼,此為工作中的lnmp一鍵安裝指令碼
3.當輸入3時,退出當前選單及指令碼提示
4.當輸入任何其他字元時,給出"Input error"後退出指令碼
5.對執行指令碼進行先關的條件判斷,例如:指令碼檔案是否存在,是否可執行等的判斷。
[root@mycentos select]# echo "lanp is installed" > /server/scripts/lanp.sh [root@mycentos select]# echo "lamp is installed" > /server/scripts/lamp.sh #!/bin/sh path=/server/scripts #設定指令碼檔案的路徑 [ ! -d "$path" ]&& mkdir -p $path#判斷是否存在,不存在就建立 function menu(){ #選單欄 cat <<END 1.[install lamp] 2.[install lanp] 3.exit input the num you want: END } function main(){ #主函式 menu #顯示選單 read num #使用者選項 expr $num + 1 &> /dev/null # 判斷使用者輸入是否是整數 if [ $? -ne 0 ];then echo "must input {1|2|3}" exit 1 fi case "$num" in 1) echo "start installing lamp" sleep 2 if [ -x "$path/lamp.sh" ];then #判斷指令碼是否可執行 source$path/lamp.sh#載入其他指令碼 exit $? else echo "$path/lamp.sh does not exist or can not be exec" exit 1 fi ;; 2) echo "start installing lanp" sleep 2 if [ -x "$path/lanp.sh" ];then source $path/lanp.sh exit $? else echo "$path/lamp.sh does not exist or can not be exec" exit 1 fi ;; 3) echo "bye" exit 3 ;; *) echo "you must input {1|2|3}" esac } main 注: 1.此處用的是cat用法,還可以用select,但是常用的是cat 的here文件