shell中的函式
阿新 • • 發佈:2020-09-02
#什麼是函式 盛放某一功能的容器 #為什麼要用函式 沒有引入函式前,遇到重複使用某一個功能的地方,只能複製黏貼實現該功能的程式碼,這會導致: 1.減少程式碼冗餘,解決指令碼重複使用某一功能,結構不清晰,可讀性差 2.可擴充套件性差,如果要修改功能,需要找到該指令碼內所有的該功能才能修改 #怎麼呼叫函式 先定義,後呼叫 把程式碼從磁碟取出來,放到記憶體(函式呼叫),在記憶體申請一個記憶體空間,把函式體程式碼放進去,通過函式名呼叫 #函式就是命令,指令碼也是命令 命令可以傳參,指令碼可以傳參,函式可以傳參 函式的執行相當於開了一個子shell,所以可以使用指令碼的位置引數 使用$$可以看到,指令碼和函式屬於一個程序
一、函式定義的格式
function name() {
statements
[return value]
}
#省略空格
function name(){
statements
[return value]
}
#省略function
name() {
statements
[return value]
}
#省略小括號
function name {
statements
[return value]
}
二、函式的執行
一:不帶引數的執行 1.function和後面的小括號都不帶嗎,僅僅時'函式名'就可以執行 2.函式的定義必須在執行之前定義或載入(即先定義後執行) 3.函式執行時,會和指令碼公用變數,也可以為函式設定區域性變數及特殊位置引數 #函式變數的作用域 4.函式中的return和指令碼中的exit + break功能類似,與break的功能相同,return用來退出函式,exit是用來退出指令碼,break用來退出迴圈,continel用來退出本次迴圈 5.return的返回值('只能是0-255的整型')會給呼叫函式的程式,而exit的返回值給執行程式的shell #指令碼程序。$?可以檢視 return的返回值使用$?檢視 return的返回值可以是變數 return後面的同級別程式碼將不會執行 一個函式可以寫多個return,同級別值執行一個,不同級別配合if使用 return沒有echo好用 6.如果函式獨立與指令碼之外,被指令碼載入使用時,需要使用source或者"."來提前載入使用 例如: . /etc/init.d/functions #載入系統函式中的命令或者引數變數,以供後面的程式使用 #指令碼內定義函式的話,可以直接呼叫系統函式 7.local定義函式內部的區域性變數,變數在離開函式後會消失 #函式變數的作用域 二:帶引數的執行: 函式名 引數1 引數2 1.shell的位置引數$1、$2、..$# 、$*、$?及$@都可以作為函式的引數使用,此時,父指令碼的引數臨時被隱藏,而$0仍然是父指令碼的名稱 2.當函式執行完成時,原來的命令列指令碼的引數恢復 3.'函式的引數變數'是在函式體裡面定義的, #先新增後呼叫
#測試位置引數的隱藏,函式體中的位置引數表示傳入的引數 #!/bin/bash echo $0 echo $1 echo $2 echo $3 function name(){ echo $0 echo $1 echo $2 echo $3 } name #檔案返回值測試 [root@hass-11 script]# /bin/true;echo $? 0 [root@hass-11 script]# /bin/false;echo $? 1 #action格式 [root@hass-11 script]# action "註釋" /bin/true 註釋 [ OK ] [root@hass-11 script]# action "註釋" /bin/false 註釋 [FAILED] #return與exit,函式體沒有return,exit檢視的返回值預設是最後一串程式碼執行之後的返回值 #!/bin/sh function name(){ echo 111 xxxxxx echo 333 } name echo $?
小插曲
1.使用cat命令追加多行,如'列印選項選單'
cat <<END
1.FIRST
2.SECOND
3.THIRD
END
2.給輸出的字型加顏色
echo -e 識別轉義字元,這裡識別字符的特殊含義,加顏色
#顏色的開始
RED_COLOR='\E[1;31m'
GREEN_COLOR='\E[1;32m'
YELLOW_COLOR='\E[1;33m'
BLUE_COLOR='\E[1;34m'
#顏色的結束
RES='\E[0m'
echo -e ${RED_COLOR} OLD ${RES} #OLD是紅色
echo -e ${GREEN_COLOR} OLD ${RES} #OLD是綠色
echo -e ${YELLOW_COLOR} OLD ${RES} #OLD是黃色
echo -e ${BLUE_COLOR} OLD ${RES} #OLD是藍色
定義一個函式,計算所有引數之和
#!/bin/bash
function usage(){
if [ $# -lt 2 ];then
echo "usage: $0 num1 num2 ..."
fi
}
function zhengshu(){
for i in $*
do
((i++))
if [ $? -ne 0 ];then
usage
fi
done
}
function getsum(){
local sum=0
for n in $*
do
((sum+=n))
done
return $sum
}
function main(){
usage $*
zhengshu $*
getsum $*
echo $?
}
main $*
作用域
#什麼是作用域
變數的作用範圍
也就是定義一個變數,在哪可以訪問到
#為什麼要使用作用域
區分變數查詢的優先順序,避免衝突
#區域性作用域
函式內部定義
使用local關鍵字宣告
只能在"該函式內"使用
程序級別的,不同程序之間,區域性變數不通用
子函式不是子程序,是同一個程序,屬於一個函式
#!/bin/sh
unset x
x=000
function syy(){
echo $x
}
function name(){
local x=111
syy
}
function syy2(){
echo $x
}
echo $x
name
syy2
#全域性作用域
在當前程序內宣告
不使用關鍵字
可以在"當前程序的任意位置"訪問到
全域性變數是程序級別的
當前shell中執行
. ./test.sh
#!/bin/sh
unset x
x=222
function name(){
local x=111
}
echo $x
#只要沒有被local宣告的變數,都是全域性變數
#shell的變數是程序級別的,python的變數是檔案級別的
程序級別的變數'在不同的程序'是之間'不能訪問'的
檔案級別的變數在不同的程序之間是可以訪問的
shell的變數可以做成'偽檔案級別'的(使用 export 關鍵字定義,然後放到全域性環境變數檔案中)
#環境變數,檔案級別的
變數的程序隔離
[root@hass-11 ~]# unset x
[root@hass-11 ~]# x=1
[root@hass-11 ~]# echo $x
1
[root@hass-11 ~]# bash
[root@hass-11 ~]# echo $x
環境變數的"傳子不傳爹"
[root@hass-11 ~]# export x=2
[root@hass-11 ~]# exit
[root@hass-11 ~]# echo $x
1
傳子,使用export變數定義,臨時的,所有子程序中都可以使用
[root@hass-11 ~]# unset x
[root@hass-11 ~]# export x=2
[root@hass-11 ~]# bash
[root@hass-11 ~]# echo $x
2
#環境變數檔案
/etc/bashrc #推薦,永久生效
/etc/proifile
/etc/proifile.d/* #推薦
~/.bashrc
~/.bash_profile
#登入式shell與非登入式shell
登陸式:su - syy
非登陸式:su syy #不會載入所有的環境變數檔案,涉及到指令碼能否正常執行
#記憶體佔用與優化
佔用
每次定義變數都要使用記憶體空間
優化
減少bash的個數
減少變數的定義
實戰題目一:
用shell指令碼檢查某網站是否存在異常
方式一(普通):
#!/bin/bash
if [ $# -ne 1 ];then
usage $"usage $0 url"
fi
wget --spider -q -o /dev/null --tries=1 -T 5 $1
#--spider用於測試,不下載,-q不在命令中顯示 -o 輸入到後面的檔案中 ,--tries=number為嘗試次數和-t一樣 -T超時時間和--timeout一樣 -S顯示響應頭
if [ $? -eq 0 ];then
echo "$1,up"
else
echo "$1,down"
fi
方式二(函式封裝):
#!/bin/bash
function Usage(){
echo $"Usage:$0 url"
exit 1
}
function check_url(){
wget --spider -q -o /dev/null -t 1 -T 5 $1
if [ $? -eq 0 ];then
echo "ok"
else
echo "error"
fi
}
function main(){
if [ $# -ne 1 ];then
Usage
else
check_url $1
fi
}
main $* #將指令碼傳入的引數全部都傳到主函式中
實戰題目二:
引數傳入指令碼、檢查某網站是否存在異常,以更專業的方式輸出
#!/bin/bash
. /etc/init.d/functions #呼叫(載入)系統函式,因為下面要用action函式
function Usage(){
echo $"usage:$0 url"
exit 1
}
function check_url(){
wget --spider -q -o /dev/null -t 1 -T 5 $1
if [ $? -eq 0 ];then
action "test $1" /bin/true
else
action "test $1" /bin/false
fi
}
function main (){
if [ $# -ne 1 ];then
Usage
else
check_url $1
fi
}
main $*
效果如下:
[root@mycentos shell]# sh 3.sh www.baidu.com
test www.baidu.com [ OK ]
[root@mycentos shell]# sh 3.sh www.baidu.c
test www.baidu.c [FAILED]
實戰題目三:
用shell開發模組化rsync服務啟動指令碼
#!/bin/bash
#chkconfig:2345 21 81
#description
#上面2行是將rsync加入開機自啟動服務
#呼叫系統函式
. /etc/init.d/functions
#輸入錯誤提示
function Usage(){
echo "usage: $0 {start|stop|restart}"
exit 1
}
#啟動服務
function Start(){
rsync --daemon #啟動服務
sleep 2 #啟動服務2秒後再做判斷
if [ $(netstat -pantu | grep rsync | wc -l) -ne 0 ];then
action "rsyncd is started" /bin/true
else
action "rsyncd is started" /bin/false
fi
}
#停止服務
function Stop(){
killall rsync &>/dev/null
sleep 2
if [ $(netstat -apntu| grep rsync | wc -l) -eq 0 ];then
action "rsyncd is stopped" /bin/true
else
action "rsyncd is stopped" /bin/false
fi
}
case "$1" in
"start")
Start
;;
"stop")
Stop
;;
"restart")
Stop
sleep 1
Start
;;
*)
Usage
esac
結果如下:
[root@mycentos init.d]# /etc/init.d/rsyncd start
rsyncd is started [ OK ]
[root@hass-11 script]# yum install -y lsof
[root@mycentos init.d]# lsof -i:873
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
rsync 1478 root 4u IPv4 13067 0t0 TCP *:rsync (LISTEN)
rsync 1478 root 5u IPv6 13068 0t0 TCP *:rsync (LISTEN)
[root@mycentos init.d]# /etc/init.d/rsyncd stop
rsyncd is stopped [ OK ]
[root@mycentos init.d]# lsof -i:873
[root@mycentos init.d]# /etc/init.d/rsyncd restart
rsyncd is stopped [ OK ]
rsyncd is started [ OK ]
[root@mycentos init.d]# lsof -i:873
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
rsync 2379 root 4u IPv4 19734 0t0 TCP *:rsync (LISTEN)
rsync 2379 root 5u IPv6 19735 0t0 TCP *:rsync (LISTEN)
注:
1.在安裝或者啟動時,如果遇到找不到“/etc/rsync.conf”檔案時,要用touch建立一個,實際工作中要寫入東西,此處為了方便就不寫,為了啟動rsync服務即可
2.rsync具體的脫離xinetd啟動的方式見"shell基礎二"
3.放在/etc/init.d目錄下就可以用service命令啟動,啟動前需要是指令碼有執行許可權,否則無法執行,也無法自動補全。
4.要加入開機自啟動,則需要加入指令碼中直譯器下面2行,然後“chkconfig --add rsyncd”加入自啟動 "rsyncd"是/etc/init.d目錄下的服務名稱
實戰四:
執行shell指令碼,列印如下選單,柑橘選擇,給選擇的水果加一種顏色。
1.紅色的蘋果
2.綠色的蘋果
3.黃色的蘋果
4.藍色的蘋果
方法一(普通版)
#!/bin/sh
#定義好顏色,方便後面使用
RED_COLOR='\E[1;31m'
GREEN_COLOR='\E[1;32m'
YELLOW_COLOR='\E[1;33m'
BLUE_COLOR='\E[1;34m'
RES='\E[0m'
cat <<END
====================
1.紅色的蘋果
2.綠色的蘋果
3.黃色的蘋果
4.藍色的蘋果
====================
END
read -p "input a munber you want:" NUM
case "$NUM" in
1)
echo -e ${RED_COLOR}apple${RES}
;;
2)
echo -e ${GREEN_COLOR}apple${RES}
;;
3)
echo -e ${YELLOW_COLOR}apple${RES}
;;
4)
echo -e ${BLUE_COLOR}apple${RES}
;;
*)
echo "usage:input {1|2|3|4}"
exit 1
esac
方法二(函式封裝):
#!/bin/sh
#定義好顏色,方便後面使用
RED_COLOR='\E[1;31m'
GREEN_COLOR='\E[1;32m'
YELLOW_COLOR='\E[1;33m'
BLUE_COLOR='\E[1;34m'
RES='\E[0m'
function Menu(){
cat <<END
====================
1.紅色的蘋果
2.綠色的蘋果
3.黃色的蘋果
4.藍色的蘋果
====================
END
}
function Usage(){
echo "$usage:input a number{1|2|3|4}"
exit 1
}
function Choose(){
read -p "input a munber you want:" NUM
case "$NUM" in
1)
echo -e ${RED_COLOR}apple${RES}
;;
2)
echo -e ${GREEN_COLOR}apple${RES}
;;
3)
echo -e ${YELLOW_COLOR}apple${RES}
;;
4)
echo -e ${BLUE_COLOR}apple${RES}
;;
*)
Usage
esac
}
function Main(){
Menu
Choose
}
Main
#函式體裡面可以再次呼叫函式
效果如圖:
實戰五:緊接著上題,請開發一個給指定內容加上指定顏色的指令碼
#!/bin/bash
RED_COLOR='\E[1;31m'
GREEN_COLOR='\E[1;32m'
YELLOW_COLOR='\E[1;33m'
BLUE_COLOR='\E[1;34m'
RES='\E[0m'
function Usage(){
echo $"usage:$0 txt {red|green|yellow|pink}"
exit 1
}
if [ $# -ne 2 ];then
Usage
fi
function Choose(){
case "$2" in
"red")
echo -e ${RED_COLOR}$1${RES}
;;
"green")
echo -e ${GREEN_COLOR}$1${RES}
;;
"yellow")
echo -e ${YELLOW_COLOR}$1${RES}
;;
"blue")
echo -e ${BLUE_COLOR}$1${RES}
;;
*)
Usage
esac
}
function Main(){
Choose $1 $2
}
Main $*
注:
1.將引數二的顏色付給引數一
2.精確匹配'單詞'的三種方式
1.grep -w 'oldboy' file
2.grep "\boldboy\b" file
3.grep "^oldboy$" file
以上都是將出現oldboy單詞的行顯示出來,而不是將包含oldboy的行顯示出來
實戰五:
啟動Nginx服務的命令:/application/nginx/sbin/nginx
關閉Nginx服務的命令:/application/nginx/sbin/nginx -s stop
請開發指令碼,以實現Nginx服務啟動和關閉功能,具體指令碼命令為/etc/init.d/nginxd {start|stop|restart},並通過chkconfig進行開機自啟動
思路:
1.判斷開啟或關閉服務(一是檢測pid檔案是否存在,存在就是開啟,不存在就是服務已經關閉),或者使用netstat連結數也可以
2.start和stop分別構成函式
3.對函式和命令執行的返回值進行處理
4.chkconfig實現服務自啟動
程式碼:
#!/bin/sh
#chkconfig:2345 27 83
#description
source /etc/init.d/functions #載入系統函式庫
#定義檔案路徑
PATH="/application/nginx/sbin"
PID_PATH="/application/nginx/logs/nginx.pid"
REVEAL=0
function Usage(){
echo $"usage:$0 {start|stop|restart}"
exit 1
}
#判斷引數的個數
if [ $# -ne 1 ];then
Usage
fi
#開始函式
function Start(){
if [ ! -f $PID_PATH ];then #若原來服務是關閉的
$PATH/nginx #開啟服務
REVEAL=$?
if [ $REVEAL -eq 0 ];then #判斷是否開啟
action "nginx is started" /bin/true
else
action "nginx is started" /bin/false
fi
else
echo "nginx is running"
fi
return $REVEAL
}
function Stop(){#結束函式
if [ -f $PID_PATH ];then #若原來服務是啟動的
$PATH/nginx -s stop #關閉服務
REVEAL=$?
if [ $REVEAL -eq 0 ];then #判斷是否關閉
action "nginx is stopped" /bin/true
else
action "nginx is stopped" /bin/false
fi
return $REVEAL
else
echo "nginx is no running"
fi
return $REVEAL
}
case "$1" in
"start")
Start
REVEAL=$?
;;
"stop")
Stop
REVEAL=$?
;;
"restart")
Stop
Start
REVEAL=$?
;;
*)
Usage
esac
exit $REVEAL
效果如下:
[root@mycentos init.d]# /etc/init.d/nginxd start
nginx is running
[root@mycentos init.d]# /etc/init.d/nginxd stop
nginx is stopped [ OK ]
[root@mycentos init.d]# /etc/init.d/nginxd start
nginx is started [ OK ]
[root@mycentos init.d]# lsof -i:80
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 2928 root 6u IPv4 23795 0t0 TCP *:http (LISTEN)
nginx 2929 nginx 6u IPv4 23795 0t0 TCP *:http (LISTEN)
[root@mycentos init.d]# /etc/init.d/nginxd restart
nginx is stopped [ OK ]
nginx is started [ OK ]
[root@mycentos init.d]# lsof -i:80
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 2940 root 6u IPv4 24824 0t0 TCP *:http (LISTEN)
nginx 2941 nginx 6u IPv4 24824 0t0 TCP *:http (LISTEN)
彩蛋一枚
return :
1.用來退出函式,後面的函式裡面的內容不再執行
exit:
2.用來退出指令碼,後面的內容不再執行
function test(){
return 1
}
test #函式執行完成後,$?會得到test中return後面的返回值
ls -l ./ #這條命令執行成功,$?變成0
function test2(){
exit 99
}
test2 #函式執行完成後,$?變成99,也就是exit後面的數值
$?的值會不斷的改變,有別於其他語言的函式呼叫的返回值。
結果:
#!/bin/bash
function test(){
return 8
}
echo $? #0
test
echo $? #8
function test2(){
return 9
}
echo $? #0
test2
echo $? #9
ls -l ./
echo $? #0
exit 10
此shell執行結束後,echo $? 結果是10