10.迴圈
10.1 迴圈概述
迴圈型別 | 說明 |
for迴圈 | 最常用的迴圈,2種格式 |
while迴圈 當型迴圈 | while可以加入條件,死迴圈, 讀取檔案 |
do until 迴圈 直到迴圈 | 極少用 |
10.2 for迴圈
10.2.1 最常用的for迴圈格式
# 最常用的一種
for 變數 in 候補清單(列表)
do
命令
done
for n in 1 2 4 5 6 79 81
do
echo $n
done
第1次迴圈 變數n的值就是 1
第2次迴圈 變數n的值就是 2
第3次迴圈 變數n的值就是 4
第4次迴圈 變數n的值就是 5
.。。。。。
for n in {1..10} `seq 10` `ls /etc/`
10.2.2 c語言格式for迴圈
for((i=1;i<=3;i++))
do
echo $i
done
1
2
3
i=1 變數i初始化是1.(第1次迴圈的時候)
i<=3 i的值小於等於10的時候執行迴圈語句。(條件)
每次迴圈後執行i++ i=i+1
1.執行迴圈
2.i初始值是1
3.進行判斷i<=3,滿足條件,執行迴圈。
4.執行命令,執行完成後。
5.執行i++,然後進行下次迴圈。
6.迴圈3-5步驟
10.2.3 for迴圈格式及應用場景
for迴圈格式 | 格式 | 應用場景 |
⭐ 通用格式 |
for n in {1..10} do echo $n done |
通用,大部分場景可用。 |
c語言格式 |
for((i=1;i<=10;i++)) do echo $i done |
對陣列迴圈使用。 |
10.2.4 生成隨機字元
# 第1種方法
# debian/ubunt軟體包叫:libstring-mkpasswd-perl 命令 mkpasswd.pl
# 紅帽系列 軟體包叫: expect 命令 mkpasswd
-l 密碼長度
-d 數字數量
-s special 特殊字元
-C 大寫字母
-c 小寫字母
[root@Kylin-V10-sp3 ~]# mkpasswd -l 10 -d 0 -s 0 -C 0
elpamozizp
[root@Kylin-V10-sp3 ~]#
# 第2種方法
tr
-c取反,
-d刪除
/dev/urandom 字元裝置,生成隨機字元。
# 生成隨機數不會停,用head -c10(取前10個)
[root@Kylin-V10-sp3 ~]# tr -cd 'a-z' </dev/urandom |head -c10
kmzclqoijt[root@Kylin-V10-sp3 ~]#
[root@Kylin-V10-sp3 ~]# tr -cd 'a-zA-Z0-9' </dev/urandom |head -c10
0Jn26Y83cp[root@Kylin-V10-sp3 ~]#
# 第3種方法
uuidgen
[root@Kylin-V10-sp3 ~]# uuidgen
cb8080b6-f2ae-4f6d-a705-be4cd70d5dee
# 第4種方法
date +%N |md5sum
%N納秒
[root@Kylin-V10-sp3 ~]# date +%N |md5sum
322cdee250bc439e704ae651e86609c5 -
# 第5種方法
$RANDOM 取值範圍 0-32767
[root@Kylin-V10-sp3 ~]# echo $((RANDOM+100))
17996
[root@Kylin-V10-sp3 ~]# echo $((RANDOM+100)) | md5sum
9ee516054c3b4eaf7e7369ea37e61b84 -
[root@Kylin-V10-sp3 ~]#
10.2.5 批次建立檔案
面試題: 使用for迴圈在root/test目錄下透過隨機小寫10個字母加固定字串xk批次建立10個html檔案
#!/bin/bash
##############################################################
# File Name:random_file.sh
# Version:V1.0
# Author:xk
# Organization: none
# Desc:
##############################################################
dir=/root/test/
# 命令檢查
check_cmd(){
if ! which mkpasswd >/dev/null 2>&1;then
echo "mkpasswd 命令不存在,請安裝"
echo "debian mkpasswd.pl,紅帽mkpasswd"
exit 1
fi
}
# 檢查目錄
check_dic(){
[ ! -d $dir ] && {
mkdir -p /root/test/
}
}
# 建立檔案
touch_files(){
for i in {01..10}
do
random_str=`mkpasswd -l 10 -d 0 -s 0 -C 0`
filename=${random_str}_xk.html
touch $dir${random_str}
done
}
main(){
check_cmd
check_dic
touch_files
}
main
10.3 while迴圈
10.3.1 概述
- while迴圈(當型迴圈,當條件滿足後才能執行迴圈內容)
- 加入條件(條件測試語句)
- 死迴圈
- 讀取檔案,管道內容
10.3.2 while迴圈通用格式
while 條件 #[]條件 命令
do
命令
done
# 溫馨提示:
while迴圈只會在滿足條件後執行。
10.3.3 輸出1+2+3+4+5+...+10的結果
#!/bin/bash
##############################################################
# File Name:while_sum.sh
# Version:V1.0
# Author:xk
# Organization: none
# Desc:
##############################################################
# 1.while
i=1
sum=0
while [ $i -le 10 ]
do
let sum=sum+i
let i++
done
echo $sum
[root@Kylin-V10-sp3 /server/scripts/shell-pro]# bash while_sum.sh
55
# 2.for
sum=0
for i in {1..10}
do
let sum=sum+i
done
echo $sum
# 3.awk
[root@Kylin-V10-sp3 ~/test]# seq 10 | awk '{i+=$1} END {print i}'
55
10.3.3 while迴圈-死循
- 含義:一直迴圈,直到使用者手動中止或滿足指定條件後退出。
- 應用場景:
- 一般與使用者互動的時候或指令碼在系統後臺持續執行。
- 書寫一個5秒鐘執行1次的任務.
面試題: 每5秒鐘檢查下jd.com(網站是否可以訪問)curl/wget,失敗超過5次則輸出警告資訊(重啟)
check_url_while.sh
#!/bin/bash
##############################################################
# File Name:check_url_while.sh
# Version:V1.0
# Author:xk
# Organization: none
# Desc:
##############################################################
# 呼叫函式庫
func_include=/server/scripts/shell-pro/func_color.sh
if [ -f $func_include ];then
. $func_include
else
echo "函式庫 $func_include 不存在,請檢查路徑"
exit 1
fi
# 引數
url=$1
count=0
pid=$$ # 指令碼程序id
# 判斷引數是否為空
check_param(){
[ -z "${url}" ] && {
echo "請輸入域名$0 url"
exit 1
}
}
# 記錄pid
write_pid(){
echo "$0 $pid" >/run/scripts.pid
[ $? -ne 0 ] && {
echo "pid 寫入失敗"
exit 1
}
}
# 檢查命令
check_cmd() {
which curl >/dev/null 2>&1
if [ $? -ne 0 ];then
redecho 命令curl不存在
exit 2
fi
}
# 檢查網站
check_url(){
while true
do
curl -L $url >/dev/null 2>&1
if [ $? -ne 0 ];then
let $count++
[ $count -ge 5 ] && {
echo "網站訪問失敗"
$count=0
}
else
echo "網站訪問成功"
fi
sleep 5
done
}
main(){
check_param
write_pid
check_cmd
check_url
}
main
[root@Kylin-V10-sp3 /server/scripts/shell-pro]# bash check_url_while.sh qq.com
網站訪問成功
網站訪問成功
10.3.4 while迴圈-讀取檔案內容
- 應用場景:
- 需要在指令碼中讀取檔案內容,多行。
- 此時可以選擇3劍客或while迴圈.
使用while + read的形式去讀取檔案或管道內容.
檢視程式碼
read line #變數名字
#方式1:採用exec讀取檔案後,然後進入while迴圈處理。
exec<FILE檔案
while read line
do
cmd
echo $line
done
#方式2:使用cat讀取檔案內容,然後透過管道進入 不適用於有變數傳遞場景使用。
while迴圈處理。
cat FILE|while read line
do
cmd
echo $line
done
# 方式3:在while迴圈結尾done透過輸
#重定向指定讀取的檔案。 推薦使用.
while read line
do
cmd
done<FILE
#使用建議
如果前面是命令使用方式2即可管道形式.
如果是檔案一般使用方式3即可.
透過while read方式,統計ip.txt檔案,次數大於5,ping下.
while_read_total_ip.sh
# 準備資料
cat >/server/files/ip.txt<<EOF
10.0.0.2 5
10.0.0.36 6
10.0.0.37 8
baidu.com 10
jd.com 5
EOF
#!/bin/bash
##############################################################
# File Name:while_read_total_ip.sh
# Version:V1.0
# Author:xk
# Organization: none
# Desc:
##############################################################
src_path="/server/files/ip.txt"
func_include="/server/scripts/shell-pro/func_color.sh"
# 判斷檔案是否存在
file_isexist(){
if [ -f $func_include ];then
. $func_include
else
echo "函式庫 $func_include 不存在,請檢查路徑"
exit 1
fi
}
# 讀取檔案
read_line(){
while read line
do
#ip=`echo $line+awk取列`
ip=`echo $line| awk '{print $1}'`
count=`echo $line| awk '{print $2}'`
if [ $count -gt 5 ];then
ping -c1 $ip >/dev/null 2>&1
[ $? -eq 0 ] && greenecho "$ip 可以訪問" || redecho "$ip 不可以訪問"
fi
done <$src_path
}
main(){
file_isexist
read_line
}
main
[root@Kylin-V10-sp3 /server/scripts/shell-pro]# bash while_read_total_ip.sh
10.0.0.36 可以訪問
10.0.0.37 不可以訪問
baidu.com 可以訪問
# 如果檔案列數較少,while讀檔案也可以用如下方法
while read ip count
do
#ip=`echo $line+awk取列`
if [ $count -ge 5 ];then
ping -c 1 $ip &>/dev/null
if [ $? -eq 0 ];then
greenecho "$ip 可以訪問"
else
redecho "$ip 不可以訪問"
fi
fi
done <$src_file
while讀取檔案的方法2 vs 方法3 區別
while_read_dif.sh
#!/bin/bash
##############################################################
# File Name:while_read_dif.sh
# Version:V1.0
# Author:xk
# Organization: none
# Desc:
##############################################################
file=/server/files/ip.txt
i=0
j=0
#01
echo "方法2:while + cat"
cat $file |while read ip
do
echo $ip
let i++
done
echo "次數 $i"
#02
echo "方法3:while + 輸入"
while read ipaddr
do
echo $ipaddr
let j++
done<$file
echo "次數 $j"
[root@Kylin-V10-sp3 /server/scripts/shell-pro]# bash while_read_dif.sh
方法2:while + cat
10.0.0.2 5
10.0.0.36 6
10.0.0.37 8
baidu.com 10
jd.com 5
次數 0
方法3:while + 輸入
10.0.0.2 5
10.0.0.36 6
10.0.0.37 8
baidu.com 10
jd.com 5
次數 5
# 方法2(while read+管道)在執行的時候因為管道,建立1個子shell,變數都存放在子shell中,子shell執行完成,消失了,變數也沒了。
# 方法3執行的時候是與當前指令碼在同一個shell中,所以變數都保持了,可以繼續使用。
案例42(學了iptables後可以解決):分析ngx訪問日誌找出訪問量最高前5個 ip及他們的訪問次數,IP訪問次數大於200,透過防火牆遮蔽ip
防DOS,拒絕式服務攻擊.
DDOS分散式拒絕式服務攻擊.
CC 基於http請求攻擊.
10.3.5 小結
- while迴圈
- 一個場景就是while :或true死迴圈.
- 一個場景就是while read 讀取檔案內容.
10.4 do-until迴圈
#直到型迴圈: 條件不滿足後結束迴圈
until 條件
do
命令
命令
....
done
i=1
until [ $i -gt 10 ]
do
echo $i
let i++
done
10.5 迴圈小結
迴圈 | 格式 | 應用場景 |
for 迴圈 | for 變數 in 清單(列表 ) ;do 命令 ;done | 大部分使用 |
for((i=1;i<=10;i++)) ;do echo $i ;done | 陣列迴圈 | |
while | while 條件 ;do 命令 ;done | 死迴圈,讀檔案 |
until迴圈 | until 條件 ;do 命令;done | 很少用. |
10.6 迴圈控制語句
流程控制語句
10.6.1 概述
語句 |
含義 | 應用場景 |
exit
|
終止執行指令碼,退出返回值. exit n 範圍:0-255 |
基礎用法,判斷後加上exit 指令碼結束加上exit 用於指令碼中檢查部分(引數個數,檢查格 式,adv: rc=0 ,可以放在指令碼最後 exit $rc) |
return
|
放在函式中,終止`,函式返回值 |
寫在函式中,檢查函式命令執行是否成功。方便除錯. 返回值:最後1個命令或函式中關鍵命令. |
迴圈控制語句 |
||
break |
終止迴圈(退出迴圈),不會繼續執行剩餘迴圈 |
要在迴圈中表示退出迴圈。 |
continue |
跳過本次迴圈,進入下一次迴圈 |
要在迴圈中跳過某一次迴圈。 |
return
function check_wget() {
#$url 是for迴圈中建立的
wget --spider $url >/dev/null 2>&1
rc=$?
if [ $rc -eq 0 ];then
greenecho $url可以訪問
else
redecho $url 無法訪問
fi
return $rc
}
10.6.2 實戰
# continue
for n in {1..10}
do
[ $n -eq 5 ] && continue
echo $n
done
# break
for n in {1..10}
do
[ $n -eq 5 ] && break
echo $n
done
10.6.3 猜測數字
- 生成隨機數字,判斷數字是什麼(1-100)
- 如果輸入的數字比隨機數大,提示大了,
- 如果輸入數字比隨機數小,提示小了,
- 如果等於提示恭喜並退出指令碼
- 如果輸入錯誤則提示錯誤並讓使用者重新輸入(continue)
- 如果猜的次數超過10次則結束並輸出數字(這裡用下break退出)
guess_num.sh
#!/bin/bash
##############################################################
# File Name:guess_num.sh
# Version:V1.0
# Author:xk
# Organization: none
# Desc:
##############################################################
guess_num=$((RANDOM%100+1))
count=0
# 檢查數字
check_num(){
read -p "請輸入1-100內的數字:" num
[[ $num =~ ^[0-9]+$ ]] || {
echo "請輸入數字"
exit 1
}
}
# 比較
compare(){
while true
do
check_num
let count++
if [ $num -ne $guess_num ];then
[ $count -gt 7 ] &&{
echo "次數用完了"
break
}
if [ $num -gt $guess_num ] ;then
echo "猜測的數比隨機數大"
else
echo "猜測的數比隨機數小"
fi
continue
else
echo "猜對了,數字為 $guess_num"
break
fi
done
total
}
# 總結
total(){
#conut1=`awk -vn=$count 'BEGIN {print n - 1}'`
count1=`echo $count -1 | bc`
echo "一共猜測了 $count 次, 猜錯了 $count1 次"
}
main(){
compare
}
main
[root@Kylin-V10-sp3 /server/scripts/shell-pro]# bash guess_num.sh
請輸入1-100內的數字:50
猜測的數比隨機數小
請輸入1-100內的數字:75
猜測的數比隨機數大
請輸入1-100內的數字:62
猜測的數比隨機數大
請輸入1-100內的數字:56
猜測的數比隨機數大
請輸入1-100內的數字:53
猜測的數比隨機數大
請輸入1-100內的數字:52
猜測的數比隨機數大
請輸入1-100內的數字:51
猜對了,數字為 51
一共猜測了 7 次, 猜錯了 6 次
10.6.4 案例
10.6.4.1 return
start_stop_nginx
start_nginx() {
# Start the daemon/service
#
# Returns:
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
start-stop-daemon --start --quiet --pidfile $PID --exec $DAEMON --test > /dev/null || return 1
start-stop-daemon --start --quiet --pidfile $PID --exec $DAEMON -- $DAEMON_OPTS 2>/dev/null || return 2
}
stop_nginx() {
# Stops the daemon/service
#
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --stop --quiet --retry=$STOP_SCHEDULE --pidfile $PID --name $NAME
RETVAL="$?"
sleep 1
return "$RETVAL"
}
10.6.4.2 break&continue
break n 結束多少層迴圈
continue n 結束當前迴圈,並從第幾層執行
for i in {1..5}
for i in {1..5}
do
for j in {a..c}
do
echo $i $j
done
done
for i in {1..5}
do
for j in {a..c}
do
[ $j = b ] && continue
echo $i $j
done
done
for i in {1..5}
do
for j in {a..c}
do
[ $j = b ] && continue 2
echo $i $j
done
done
for i in {1..5}
do
for j in {a..c}
do
[ $j = b ] && break
echo $i $j
done
done
for i in {1..5}
do
for j in {a..c}
do
[ $j = b ] && break 2
echo $i $j
done
done
10.6.5 小結
要求 熟練掌握break和continue 含義即可。
熟練使用exit
逐漸在函式中使用return.
11.陣列
11.1 概述
- 陣列也是一種變數. 一組資料
- 陣列可以存放多個 相關聯內容 ,透過訪問陣列呼叫結果(值)
- 應用場景:
- 用於存放相關的資料。
- 獲取使用者連續的輸入.
- 如何建立陣列(手動)
# 建立陣列
[root@Kylin-V10-sp3 ~]# ip_array=(10.0.0.61 10.0.0.71 10.0.0.81)
# 輸出陣列中的內容(ip_array[0] 陣列中的某個元素 0表示下標,元素名稱.)
[root@Kylin-V10-sp3 ~]# echo ${ip_array[0]}
10.0.0.61
[root@Kylin-V10-sp3 ~]# echo ${ip_array[2]}
10.0.0.81
# 輸出陣列中所有值。
[root@Kylin-V10-sp3 ~]# echo ${ip_array[*]}
10.0.0.61 10.0.0.71 10.0.0.81
[root@Kylin-V10-sp3 ~]# echo ${ip_array[@]}
10.0.0.61 10.0.0.71 10.0.0.81
# 計算陣列的元素的個數
[root@Kylin-V10-sp3 ~]# echo ${#ip_array[*]}
3
[root@Kylin-V10-sp3 ~]# echo ${#ip_array[@]}
3
[root@Kylin-V10-sp3 ~]
shell陣列,下標或元素名字,預設只能是數字。
要在shell數字中,下標中使用字串,需要建立關聯陣列。(awk中使用更多,預設就是這個)
shell陣列的迴圈
# 推薦的形式 ⭐
[root@Kylin-V10-sp3 ~]# names=(jd.com baidu.com taobao.com 12306.cn)
[root@Kylin-V10-sp3 ~]# for n in ${names[@]}
> do
> ping -c 1 -W 1 $n
> done
PING jd.com (211.144.24.218) 56(84) bytes of data.
64 bytes from 211.144.24.218 (211.144.24.218): icmp_seq=1 ttl=128 time=7.30 ms
--- jd.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 7.302/7.302/7.302/0.000 ms
PING baidu.com (110.242.68.66) 56(84) bytes of data.
64 bytes from 110.242.68.66 (110.242.68.66): icmp_seq=1 ttl=128 time=11.7 ms
--- baidu.com ping statistics ---
...
[root@Kylin-V10-sp3 ~]#
#其他形式
names=(jd.com baidu.com taobao.com 12306.cn)
for((i=0;i<${#names[@]};i++))
do
ping -c 1 -W 1 ${names[i]}
done
11.2 shell陣列賦值
shell陣列 建立(賦值) |
具體形式 | 應用場景 |
批次直接賦值
|
array=(ip01 ip02 ip03 ip04 ) ⭐ array=( 命令結果 ) # cat ip.txt 三劍客命令獲取指定內容 |
應用最多. |
逐個元素賦值
|
array[0]=a array[1]=b array[2]=c ...... |
幾乎不會使用. |
read命令賦值 |
read -p "輸入陣列中內容:" -a array 可以建立陣列,空格分割即可. |
用於連續讀取資料,使用者輸入3個數 |
賦值
[root@Kylin-V10-sp3 /server/files]# cat >/server/files/ip2.txt<<EOF
> 10.0.0.5
> 10.0.0.6
> 10.0.0.7
> 10.0.0.8
> 10.0.0.9
> EOF
[root@Kylin-V10-sp3 /server/files]# ip_list=(`cat ip2.txt`)
[root@Kylin-V10-sp3 /server/files]# echo ${ip_list[0]}
10.0.0.5
[root@Kylin-V10-sp3 /server/files]# echo ${ip_list[*]}
10.0.0.5 10.0.0.6 10.0.0.7 10.0.0.8 10.0.0.9
[root@Kylin-V10-sp3 /server/files]# echo ${#ip_list[@]}
5
[root@Kylin-V10-sp3 /server/files]#
11.3 小結
- 如何賦值(如何建立)(透過命令結果)。
- 如何取值,統計陣列元素的個數。
- 陣列與for迴圈。
- 未來常用的是awk陣列。
12. 堡壘機
12.1 shell指令碼實現
功能:
1. 登入堡壘機的機器,自動執行堡壘機指令碼
2. 顯示選單選擇功能
2.1 遠端連線
1 web01
2 web02
3 lb01
4 db01
5 nfs01
2.2 測試ping,telnet
ping
telnet
# 不能給登入堡壘機的使用者root許可權,給了root許可權也要讓無法執行命令.
12.2 堡壘機核心功能
環境準備:
先配置好堡壘機到其他機器的金鑰認證.
書寫指令碼:
備註:
整體功能OK:
存在問題:
🅰 指令碼需要手動執行.
🅱 指令碼一次性.
12.3 自動執行指令碼
指令碼放在/etc/profile.d/xxxx.sh結尾即可.
使用者連線執行這裡面的指令碼.
軟連線過去,除錯完成可以複製
ln -sf /server/scripts/devops-shell/39.bao.sh /etc/profile.d/
設定死迴圈
12.4 指令碼禁止取消或結束
遮蔽ctrl+c
- trap 獲取指定的訊號. 用於獲取指定操作產生的訊號.
- ctrl + c對應的訊號SIGINT
- 透過trap獲取SIGINT訊號(ctrl+c),獲取後不執行任何任務.
#透過trap獲取SIGINT訊號(ctrl+c),獲取後不執行任何任務.
trap '' SIGINT
指令碼開始.
12.5 完整版本程式碼
baoleiji.sh
[root@m01-devops-shell /server/scripts/devops-shell]# cat
39.bao.sh
#!/bin/bash
#############################################################
#
# File Name:39.bao.sh
# Version:V1.0
# Author:xk
# Organization:
# Desc:
#############################################################
#
#0.trap
trap '' SIGINT
#1. main_menu主選單
main_menu() {
clear
cat <<EOF
#############堡壘機-主選單############
a: 遠端連線
#############################################
EOF
}
ssh_menu() {
clear
cat <<EOF
#############堡壘機-遠端連線選單############
1. 連線 10.0.0.5
2. 連線 10.0.0.7
3. 連線 10.0.0.51
#############################################
EOF
}
test_menu() {
clear
cat <<EOF
#############堡壘機-除錯工具選單############
1. ping
2. telnet
#############################################
EOF
}
#2.input獲取使用者輸入
input() {
read choice
}
#test部分
#ping
check_ping() {
read -p "請輸入ping的ip:" ip
#還可以加入判斷
ping -c2 -i0.5 $ip
#還可以加入判斷返回值
}
#telnet
check_telnet() {
read -p "請輸入ping的ip和埠" ip port
#還可以加入判斷
telnet $ip $port
#還可以加入判斷返回值
}
#ssh_all
ssh_all() {
#遠端連線
ssh_menu
input
case "$choice" in
1) ssh root@10.0.0.5 ;;
2) ssh root@10.0.0.7 ;;
3) ssh root@10.0.0.51 ;;
*)
echo "選擇錯誤,請重新輸入"
continue
esac
}
#test_all
test_all() {
#執行測試命令
test_menu
input
case "$choice" in
1) check_ping ;;
2) check_telnet ;;
*)
echo "選擇錯誤,退出"
continue
esac
}
#3.main
main() {
while true
do
main_menu
input
case "$choice" in
a|A) ssh_all ;;
b|B) test_all ;;
oldboy) break ;;
*)
echo error
continue
esac
done
}
main
13. Debug全流程
13.1 書寫習慣
# 書寫習慣
1. 註釋步驟,變數,函式.
2 變數:在指令碼中儘量使用變數,變數命名規範(給變數加上說明)。
3. 函式:程式碼中儘可能使用函式,增加說明。 模組化.
4. 返回值: 儘可能增加函式return功能和日誌功能,方便後期除錯。
5. 引數與選項檢查: 進可能增加exit 返回值的功能,方便後期除錯。
6. 書寫的時候適當增加輸出:書寫程式碼的時候,可以多寫一些echo用於在某些步驟中進行輸出。
7. 縮排:程式碼注意縮排。
8. 成對的符號提前輸入好. "" [] {}
13.2 除錯方法
# 除錯方法
1. x大部分時候使用:sh/bash -x顯示詳細的執行過程。
2. 精確顯示過程指令碼使用functions函式庫,指令碼過大,可以在指令碼中使用
set -x 開始顯示詳細資訊
程式碼(一般為main中呼叫的函式名)
set +x 關閉顯示詳細資訊
3. 註釋法使用函式,註釋法。註釋多餘的函式,排除。縮小範圍定位問題
4.輸出關鍵變數變數後增加echo輸出變數內容。檢視過程。
# 書寫指令碼指定為英文
export LANG=en_US.UTF-8
shell的嚴格模式,變數不存在則退出,命令執行失敗則退出
shell的嚴格模式
set -eu #
-e 只要執行失敗,就結束指令碼.就相當於exit
-u 呼叫了未定義的變數,則報錯
# 大部分時候使用:sh -x顯示詳細的執行過程。
# 排除法
# 命令列也可以用除錯
[root@Kylin-V10-sp3 /server/files]# set -x
++ printf '\033]0;%s@%s:%s\007' root Kylin-V10-sp3 /server/files
[root@Kylin-V10-sp3 /server/files]#
++ printf '\033]0;%s@%s:%s\007' root Kylin-V10-sp3 /server/files
[root@Kylin-V10-sp3 /server/files]# ls
+ ls --color=auto
array-add.txt awk-array.txt ip2.txt ip.txt urls url.txt
++ printf '\033]0;%s@%s:%s\007' root Kylin-V10-sp3 /server/files
[root@Kylin-V10-sp3 /server/files]# set +x
+ set +x
[root@Kylin-V10-sp3 /server/files]#
14. shell(三劍客)
14.1 sed與變數
14.1.1 sed命令中如何呼叫變數
cat > ip.txt<<EOF
10.0.0.5
10.0.0.6
10.0.0.7
10.0.0.8
10.0.0.9
EOF
src=10.0.0
dst=172.16.1
sed "s#$src#$dst#g" ip.txt
172.16.1.5
172.16.1.6
172.16.1.7
172.16.1.8
172.16.1.9
14.2 awk
14.2.1 指哪打哪
# 過濾出/etc/passwd的第2到9行的第1列和第3列
[root@Kylin-V10-sp3 ~]# awk -F: 'NR>=2 && NR<=9{print $1,$3}' /etc/passwd |column -t
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7
mail 8
[root@Kylin-V10-sp3 ~]#
# 第1列使用者名稱等於root的最後一列(命令直譯器)。
[root@Kylin-V10-sp3 ~]# awk -F: '$1=="root"{print $NF}' /etc/passwd
/bin/bash
# 進階:第1列使用者名稱等於root的最後一列(命令直譯器) 使用變數方法
'''
命令列/shell變數
name=root
-v n變數=內容
取出/etc/passwd中第1列 等於root行.
'''
[root@Kylin-V10-sp3 ~]# awk -vn=$name '$1==n{print $NF}' /etc/passwd
[root@Kylin-V10-sp3 ~]# awk -F: -vn=$name '$1==n{print $NF}' /etc/passwd
[root@Kylin-V10-sp3 ~]# awk -F: -vn=$name '$1~n{print $NF}' /etc/passwd | head -3
/bin/bash
/sbin/nologin
/sbin/nologin
# 過濾出網路卡檔案中ip地址那行
[root@Kylin-V10-sp3 ~]# awk -F= '/IPADDR/{print $2}' /etc/sysconfig/network-scripts/ifcfg-eth0
10.0.0.36
14.2.2 awk的判斷與迴圈
14.2.2.1 判斷
如果系統根分割槽磁碟使用率大於80,提示磁碟空間不足
if (條件) {
滿足條件執行什麼內容
}
if (條件) {
滿足條件執行什麼內容
}
else {
}
df -h|awk '$NF=="/"{ if($5>=8) print "磁碟空間不足" }'
磁碟空間不足
df -h|awk '$NF=="/"{ if($5>=8) print "磁碟空間不足" }'
awk '$NF=="/"{
if($5>=8)
print "磁碟空間不足"
}'
awk 'NR<=5' /etc/passwd
awk '{if(NR<=5){print $0}}' /etc/passwd
awk '{
if(NR<=5){
print $0
}
}' /etc/passwd
[root@m01 ~]# df -h |awk -F"[ %]+" '$NF=="/"{if( $(NF-1)>=5 )
print "磁碟空間不足" }'
磁碟空間不足
[root@m01 ~]#
[root@m01 ~]#
[root@m01 ~]#
[root@m01 ~]#
[root@m01 ~]# df -h |awk -F"[ %]+" '$NF=="/" && $(NF-1) >=80
{ print "磁碟空間不足" }'
溫馨提示:
# awk進行統計計算的時候,如果是數字+字元,變成字串對比。左到右一個字元一個字元對比。
解決方案01⭐ :避免這個情況,透過awk指定分隔符,只獲取數字部分即可。
解決方案02:透過讓這個字串進行運算,運算後會被轉換為數字。
df -h|awk '$NF=="/"{ if($5+0 >= 8) print $0 }'
14.2.2.2 迴圈
for(i=1;i<=100;i++) {
sum=sum+i
}
print sum
awk 'BEGIN{for(i=1;i<=100;i++) {sum=sum+i} print sum }'
awk 'BEGIN{
for(i=1;i<=100;i++)
{sum=sum+i}
print sum
}'
# 計算任意數字的總和.
[root@m01 ~]# read -p "請輸入數字空格分割:" num
請輸入數字空格分割:1 2 4 5 66
[root@m01 ~]# echo $num
1 2 4 5 66
[root@m01 ~]# echo $num |awk '{for(i=1;i<=5;i++) sum=sum+$i ;
print sum }'
78
[root@m01 ~]# echo $num |awk '{for(i=1;i<=NF;i++) sum=sum+$i ;
print sum }'
78
14.2.3 陣列
14.2.3.1 概述
- awk陣列專用於統計與分析。
- 去重統計次數(sort+uniq)。
- 去重求和。
- awk陣列與shell陣列區別
- awk陣列: 關聯陣列,下標啥都行。
- shell陣列:普通陣列,下標數字,shell中也有關聯陣列.。
14.2.3.2 使用
# 建立
[root@Kylin-V10-sp3 ~]# awk 'BEGIN{array[0]="a";array["name"]="abc";array[110]="sos" }'
# 取值
[root@Kylin-V10-sp3 ~]# awk 'BEGIN{array[0]="a";array["name"]="abc";array[110]="sos"; print array[0]}'
a
[root@Kylin-V10-sp3 ~]# awk 'BEGIN{array[0]="a";array["name"]="abc";array[110]="sos"; print array["name"]}'
abc
14.2.3.2.1 批次賦值
# 建立以url為下標,元素值是次數陣列。輸出陣列內容。
cat >/server/files/awk-array.txt<<EOF
baidu.com 6
qq.com 7
aliyun.com 99
EOF
[root@Kylin-V10-sp3 ~]# awk '{ip_array[$1]=$2} END {for (url in ip_array) print url ip_array[url]}' /server/files/awk-array.txt
aliyun.com99
baidu.com6
qq.com7
[root@Kylin-V10-sp3 ~]#
14.2.3.2.1 去重統計次數 (sort+uniq)
[root@Kylin-V10-sp3 ~]# cat >/server/files/url.txt<<EOF
> http://www.baidu.org/index.html
> http://www.baidu.org/1.html
> http://post.baidu.org/index.html
> http://mp3.baidu.org/index.html
> http://www.baidu.org/3.html
> http://post.baidu.org/2.html
> EOF
[root@Kylin-V10-sp3 ~]#
[root@Kylin-V10-sp3 ~]# awk -F'/+' '{url[$2]=url[$2]+1}END{ for(name in url )print name,url[name]}' /server/files/url.txt
mp3.baidu.org 1
post.baidu.org 2
www.baidu.org 3
[root@Kylin-V10-sp3 ~]# awk -F'/+' '{url[$2]++}END{ for(name in url )print name,url[name]}' /server/files/url.txt
mp3.baidu.org 1
post.baidu.org 2
www.baidu.org 3
[root@Kylin-V10-sp3 ~]#
[root@Kylin-V10-sp3 ~]# cat >/server/files/array-add.txt<<EOF
> a 1
> b 2
> c 3
> a 4
> a 5
> b 6
> d 7
> EOF
[root@Kylin-V10-sp3 ~]#
[root@Kylin-V10-sp3 ~]# awk '{array[$1]++} END {for(i in array) print i,array[i]}' /server/files/array-add.txt
a 3
b 2
c 1
d 1
# 第二列數字加起來
[root@Kylin-V10-sp3 ~]# awk '{array[$1]=array[$1]+$2} END {for(i in array) print i,array[i]}' /server/files/array-add.txt
a 10
b 8
c 3
d 7
14.2.3.2.1 統計access.log中每個ip地址的流量總數(awk陣列)
# 第1列是ip地址 第10列是流量(單位是位元組)
[root@Kylin-V10-sp3 ~/test]# awk '{flow_array[$1]=flow_array[$1]+$10} END {for (i in flow_array) print i, flow_array[i]}' access.log | sort -rnk2 | head
114.83.184.139 31362956
117.136.66.10 22431302
116.216.30.47 21466000
223.104.5.197 21464856
116.216.0.60 19145329
114.141.164.180 17219553
114.111.166.22 17121524
223.104.5.202 16911512
116.228.21.187 15969887
112.64.171.98 15255013
[root@Kylin-V10-sp3 ~/test]#
#本質:ip地址是一樣的然後把第10列相加。
flow_array[$1]=flow_array[$1]+$10
flow_array[$1]+=$10
sum=sum+$10 === sum+=$10
14.2.4 awk與shell語法格式對比
具體語法 |
awk | shell程式設計 |
if單分支判斷 |
if (條件) {命令,多個命令} |
if 條件;then 命令 fi |
if雙分支判斷 |
if (條件) {命令} else {命令} |
if 條件;then 命令 else 命令 fi |
for迴圈:c語言形式 |
for(i=1;i<=10;i++) {命令;命令2} |
for((i=1;i<=10;i++)) do 命令 done |
for迴圈:通用 |
awk專用於陣列的迴圈 for( n in 陣列名字) print n(變數n陣列下標),陣列名字[n] |
for n in 清單 do 命令 done |
15. 總結
- shell程式設計環境
- shell執行方式sh/bash
- 變數取值,賦值,命名.
- 普通變數${} ,環境變數(瞭解)
- 案例-登入系統後輸出基本資訊。
- 特殊變數 $n,$0,$#,$?
- 其他特殊變數:能夠認識,知曉含義即可.
- 案例:多種命令,多種傳參方式實現計算器