shell編程進階篇
??有人會說,shell簡單啊,就是一些命令的堆砌啊,是的一些簡單的操作僅僅執行幾個命令就行了,但是相對一些復雜的業務和要求下,如果只能做到命令的堆砌,那也太有損shell的名聲了。
??小編通過一個例子給大家介紹,如果沒有邏輯和條件,只是命令的堆砌,那麽對維護Linux,是多麽大的災難。
#需求:清除/var/log 下message 日誌文件
命令堆砌
#!/bin/bash #shell 腳本的標識 cd /var/log #進入/var/log cat /dev/null > message #清空message日誌 echo Logs cleaned up’ #打印清空成功
缺陷:
- 如果不是root用戶不能執行進入/var/log ,和清空message文件
- 不論是否成功下面的命令都繼續執行
- 不論腳本是否成功,都會輸出‘clean up’
修改後的腳本:
#!/bin/bash #shell 腳本的標識 LOG_DIR=/tmp/log #設置一個變量,名稱為/tmp/log ROOT_UID=$UID #設置一個變量為當前的用戶的uid if [ "$UID" -ne 0 ] #判斷是否是root,root用戶的uid為0 then echo "Must be root tu run this script" #如果不是輸出,並異常退出腳本 exit 1 fi cd $LOG_DIR || { #如果是root,在判斷是否可以進入/tmp/log echo "Cannot change to necessary directory." >&2 #如果不能進入,則也異常退出 exit 1 } cat /dev/null>message && echo "Log clean up." #最後清空之後,輸出一句話並正常退出 exit 0
有些初學者,看見上面的腳本,可能覺得,有些看不懂,沒關系,接下來,小編將會一一講解上面腳本的知識點!
一、條件測試
??在bash的各種流程控制結構中通常要進行各種測試,然後根據測試結果執行不同的操作,有時候也會通過if流程控制語句相結合,使我們可以方便的完成判斷。
語法:
格式1:test<測試表達式>
格式2:[ 測試表達式 ]
格式3:[[測試表達式]]
提示:在[[]]中,可以使用&&、||、>、<等操作符進行復雜判斷,但是不能應用於[]
(1)test條件測試
#使用方法: test -f file 判斷是否是文件 例:test -f file && echo 1 || echo 0 #判斷文件是否存在,存在返回 1 不存在返回0 test ! -f file 判斷文件是否不存在 例:test !-f file && echo 1 || echo 0 #判斷文件是否存在,不存在返回1 存在返回0 test -d dir 判斷是否是目錄
(2)[]條件測試
#使用方法:
[-f file] 普通文件是否存在
例:[-f file ] && echo 1 || echo 0 #判斷文件是否存在,存在返回 1 不存在返回0
[ ! -f file ] 判斷文件是否不存在
例:[-f file] && echo 1 || echo 0 #判斷文件是否存在,不存在返回1 存在返回0
[-d dir] #判斷是否是目錄
-s # 判斷文件是否存在並且不為空
-e #判斷文件是否存在,區別於-f,-e表示所有文件
-r #判斷文件是否存在並且是否可讀
-w #判斷文件是否存在並且是否可寫
-x #判斷文件是否存在並且是否可執行
-L #判斷文件是否存在並且是否為鏈接文件
f1 -nt f2 #判斷是否f1比f2新
f1 -ot f2 #判斷是否f1比f2久
(3)[[]]條件測試
#使用方法:
[[-f file]] 文件是否存在
例:[[-f file ]] && echo 1 || echo 0 #判斷文件是否存在,存在返回 1 不存在返回0
[[ ! -f file ]] 判斷文件是否不存在
例:[[-f file]] && echo 1 || echo 0 #判斷文件是否存在,不存在返回1 存在返回0
(4)字符串測試操作符
??註意:在使用字符串進行比較時,最好使用””雙引號將其擴起
(5)整數操作符
(6)邏輯操作符
(7)條件測試的舉例
??上面講了諸多概念,現在通過具體的例子讓大家看看條件測試具體怎麽用:
#這裏聲明兩個變量:file1=/etc/password file2=/etc/service
[-f “$file1”] && echo 1||echo 0 $file1存在返回1,不存在返回0
[-d “$file1”] && echo 1||echo 0 $file1是目錄返回1,不是目錄返回0
[-s “$file1”] && echo 1||echo 0 $file1是文件並且不為空返回1,否則返回0
[-e “$file1”] && echo 1||echo 0 $file1存在返回1,不存在返回0
[-w “$file1”]&&echo 1 ||echo 0 $file1存在並且可寫,返回1,不存在返回0
[-x “file1”]|| exit 5 判斷$file1是否可執行,不可執行異常退出
#對字符串的操作
[-z “$value”] || value=”…” 判斷變量是否長度為0,如果為0,賦初值
[“$networkworking”!= “yes”]&&exit 6 通過對變量的判斷,確定是否執行腳本後面的代碼
#復合條件測試
[-f “$file1”-o -e “$file2”] &&echo 1 ||echo 0 file1是文件或者file2存在返回1,否則0
[-f “$file1”-a -e “$file2”] &&echo 1 ||echo 0 file1是文件並且file2存在返回1,否則0
[[-f “$file1”&& -e “$file2”]] &&echo 1 ||echo 0 file1是文件或者file2存在返回1,否則0
[[-f “$file1”|| -e “$file2”]] &&echo 1 ||echo 0 file1是文件並且file2存在返回1,否則0
#整數測試
[ 1 -eq 2 ] &&echo 1||echo 0 等於
[ 1 -gt 2 ] &&echo 1||echo 0 大於
[ 1 -lt 2 ] &&echo 1||echo 0 小於
[ 1 -le 2 ] &&echo 1||echo 0 小於等於
[ 1 -ge 2 ] &&echo 1||echo 0 大於等於
[ 1 -ne 2 ] &&echo 1||echo 0 不等於
註意:及時這裏的兩個數組都是字符串的形式,依然可以比較。
#判斷條件後面執行多條命令
file=/etc/profile
file_bak=${file}.bak
[ -f $file ] && {
cat $file
cp $file $file_bak
mv $file_bak ~/
}
二、控制流程語句
?? 當然,僅僅學習了條件測試,就可以完成很多的功能,但是如果業務相當復雜,功能相當繁多,只是使用條件測試話,不僅完不成具體的功能,對代碼的可讀性也很差。為了勝任更高的要求,接下來介紹控制流程語句,可以說shell有了它,如魚得水吧。
(1)if語句
單分支
#語法:
If [條件]
then
命令;
命令;
….
fi
或者:if[條件];then 命令…; fi
#例:比較大小
num1=10
num2=23
if [ $num1 -gt $num2 ]
then
echo "$num1"
else
echo "$num2"
fi
#判斷系統內存大小,如果小於100M就報警
cur_free=`free -m|awk ‘ /buffers\// {print $NF}‘`
chars="current memory is $cur_free"
if [ $cur_free -lt 100 ]
then
echo "$chars"
echo "momory is unfree!"
fi
多分支
#語法:
多分支:
If [條件]
then
命令;
命令;
….
else
命令;
命令;
….
fi
#例:判斷文件是否存在,不存在就創建
file=/tmp/zy/zy.txt
if [ -f "$file" ]
then
ll $file
else
touch $file
fi
if-elif-elif...-else:
#語法:
if [條件]
then
命令;
命令;
elif [條件]
then
命令
else
命令
fi
#例:比較兩個數的大小
read -p "pls input two numbers:" a b
if [ $a -gt $b ]
then
echo "$a > $b"
elif [ $a -eq $b ]
then
echo "$a == $b"
else
echo "$a < $b"
fi
(2)case語句
#語法:
case:
語法:
case “字符串變量”in
pat1) 指令;;
pat2) 指令;;
pat3) 指令;;
*)指令
;;
esac
#例:
read -p "Please you input is a num:" num
case "$num" in
9[0-9]) #範圍查找
echo A;;
8[0-9])
echo B;;
*)
echo C;;
esac
(3)for循環
for 循環,具體的使用方法有很多,這裏我們使用三個例子說明:
#例1:(命令作為循環內容)
for i in `seq 10`
do
echo $i
done
#例2:(序列作為循環內容)
sum=0
for i in {1..100}
do
let sum=sum+i
done
echo $sum
#例3:(集合算術操作符的for)
sum=0
for((i=1;i<=100;i++))
do
let sum=sum+i
done
echo $sum
(3)while循環
#語法
while [expression]
do
指令
done
#舉例:
#例1
i=1
sum=0
while ((i<=100))
do
let sum=sum+i
let i++
done
echo $sum
#例2
i=1
sum=0
while [ $i -le 100 ]
do
let sum=sum+i
let i++
done
echo $sum
(4)until循環
#語法
unitl [ expression]
do
commond;
done
#舉例:
sum=0
i=1
until [ $i -gt 100 ]
do
let sum=sum+i
let i++
done
echo $sum
註意:unit和for、while的區別就是,until是條件滿足時退出循環,而while和for是條件不滿足是退出循環。
三、shell中的數組
(1)數組的定義:
方法1:array=(value1 value2 valu3 value4)
方法2:array=([index1]=value1 [index2]=value2 [index3]=value3)
方法3:array[0]=value1 array[1]=value2 array[2]=value
方法4:使用命令的方式定義數組:array=($(ls)) 將ls命令的執行結果當做數組的值
(2)數組的增、刪、改、查
arr=(1 2 3 4 5 6 7) #數組的聲明,並賦值
#增
arr[index]=value #給數組增加元素,index:下標(任意)
#刪
unset arr[index] #刪除相應index位置的元素
unset arr #刪除整個數組
#改
arr[index]=value #給數組增加元素,index:下標(該位置已存在值)
#查:
echo ${arr[index]} #獲取數組單個元素,index表示數組的下標
echo ${arr[\*]} #獲取數組的所有元素
echo ${arr[\@]} #獲取數組的所有元素
echo ${!arr[\*]} #獲取數組的所有下標
echo ${#arr[@]} #查看數組的長度
(3)數組的高級操作
數組內容的截取
? 語法:${arr[*]:number1:len} 從下標number1開始取,取長度為len,獲得一個新的數組。
? 例:
[root@test zy]# arr1=(1 2 3 4)
[root@test zy]# echo ${arr1[*]:0:2} #從下標為0開始取,取兩個元素
數組的替換
? 語法:${arr[*]/valu1/valu2} #將數組中的某個值,替換成其他值(完全替換),生成新數組,數組本身不變。
? 例:
[root@test zy]# arr1=(aa aa bb cc dd)
[root@test zy]# echo ${arr1[*]/aa/bb} #打印bb bb bb cc dd
數組的合並
? 語法:arr=(${arr1[*]} ${arr2[*]}) #arr1和arr2分別是兩個數組
? 例:
[root@test zy]# arr1=(1 2 3)
[root@test zy]# arr2=(4 5 6)
[root@test zy]# arr=(${arr1[]} ${arr2[]})
[root@test zy]# echo ${arr[*]} #打印:1 2 3 4 5 6
數組的遍歷
#使用下標方式遍歷:
arr=(1 2 3 4 5 6)
for i in “${!arr[*]}”
do
echo “${arr[$i]}”
done
#使用數組的值遍歷
for i in “${arr[*]}”
do
echo “$i”
done
(4)數組的存儲
註意:Shell中的數組的存儲方式不同,如果在相應的下標中無元素,則無此下標。
四、Shell中的函數
?語法
#簡答語法:
函數名(){
指令…
return n
}
#標準語法
function 函數名(){
指令…
return n
}
?shell函數的執行
#例子
#!/bin/sh
#無參數
function func_test1(){
return 2
}
#有參數
function func_test2(){
let sum=$1+$2 #這裏的$1和$2暫時作為函數的參數
return $sum
}
#調用參數函數
func_test1
echo "$?"
#調用有參數的函數
func_test2 1 2
echo "$?" #$?用於接受函數的返回值
?自定義函數庫
#第一步:編寫一個腳本(函數庫):
#!/bin/sh
function func1(){…}
function func2(){…}
function func3(){…}
#第二步:(在其他腳本中加載這個函數庫)
#!/bin/sh
source ./函數庫名 #下載函數庫
func1() #可以使用函數庫中的函數
?函數使用的註意事項:
? ? - 在函數體中,位置參數($1、$2、$3、$#、$*、$@、$?)都可以是函數的參數
? ? - 父腳本的參數則臨時的被函數參數所覆蓋(相當於局部變量)
? ? - $0 仍然是父腳本的名稱
? ? - return 關鍵字用於跳出函數
? ? - 函數調用不能在函數創建之前
? ? - 函數的返回值只能是數字,不然會報錯 :numeric argument required
五、Shell腳本的調試
? ? - 調試自己寫的腳本的方法有很多,這裏小編送大家一張圖:
shell編程進階篇