函數介紹
函數function是由若干條shell命令組成的語句塊,實現代碼 重用和模塊化編程 它與shell程序形式上是相似的,不同的是它不是一個單獨的 進程,不能獨立運行,而是shell程序的一部分
函數和shell程序比較相似,區別在於:
Shell程序在子Shell中運行
而Shell函數在當前Shell中運行。因此在當前Shell中,函 數可以對shell中變量進行修改
定義函數
函數由兩部分組成:函數名和函數體
語法一:
f_name (){
...函數體...
}
語法二:
function f_name {
...函數體...
}
語法三: f
unction f_name () {
...函數體...
}
function的幫助信息
root@(6-1)app# help function
function: function name { COMMANDS ; } or name () { COMMANDS ; }
Define shell function.
Create a shell function named NAME. When invoked as a simple command,
NAME runs COMMANDs in the calling shell's context. When NAME is invoked,
the arguments are passed to the function as $1...$n, and the function's
name is in $FUNCNAME.
Exit Status:
Returns success unless NAME is readonly.
函數使用
函數的定義和使用:
可在交互式環境下定義函數
可將函數放在腳本文件中作爲它的一部分
可放在只包含函數的單獨文件中
調用:函數只有被調用纔會執行
調用:給定函數名
函數名出現的地方,會被自動替換爲函數代碼
函數的生命週期:被調用時創建,返回時終止
函數有兩種返回值:
函數的執行結果返回值:
(1) 使用echo等命令進行輸出
(2) 函數體中調用命令的輸出結果
函數的退出狀態碼:
(1) 默認取決於函數中執行的最後一條命令的退出狀態碼
(2) 自定義退出狀態碼,其格式爲:
return 從函數中返回,用最後狀態命令決定返回值
return 0 無錯誤返回。
return 1-255 有錯誤返回
交互式環境下定義和使用函數
示例:
dir() {
> ls -l >
}
定義該函數後,若在$後面鍵入dir,其顯示結果同ls -l的 作用相同
dir
該dir函數將一直保留到用戶從系統退出,或執行了如下所 示的unset命令:
unset dir
在腳本中定義及使用函數
函數在使用前必須定義,因此應將函數定義放在腳本開始部分,直至 shell首次發現它後才能使用
調用函數僅使用其函數名即可
示例:
[root@localhost hanshu]# cat linshi
#!/bin/bash
#fun1
holle() #定義函數holle
{
echo "holle there taday's date is `date +%F`" #函數體
}
echo "now going to the function hello"
holle #腳本自身調用函數
echo "back from the function"
[root@localhost hanshu]# .linshi #調用函數的輸出
now going to the function hello
holle there taday's date is 2018-03-21
back from the function
使用函數文件
可以將經常使用的函數存入函數文件,然後將函數文件載 入shell
文件名可任意選取,但最好與相關任務有某種聯繫。例如 :functions.main
一旦函數文件載入shell,就可以在命令行或腳本中調用 函數。可以使用set命令查看所有定義的函數,其輸出列 表包括已經載入shell的所有函數
若要改動函數,首先用unset命令從shell中刪除函數。改動完畢後,再重新載入此文件
創建函數文件
函數文件示例
cat functions.main
#!/bin/bash
#functions.main
findit() #定義函數 findit
{
if [ $# -lt 1 ] ; then
echo "Usage:findit file"
return 1
fi
find / -name $1 –print
}
載入函數
函數文件已創建好後,要將它載入shell
定位函數文件並載入shell的格式:
. filename 或 source filename
注意:此即<點> <空格> <文件名> 這裏的文件名要帶正確路徑
示例:
上例中的函數,可使用如下命令:
. functions.main
檢查載入函數
使用set命令檢查函數是否已載入。set命令將在shell中顯示 所有的載入函數
示例:
set
findit=( )
{
if [ $# -lt 1 ]; then
echo "usage :findit file";
return 1
fi
find / -name $1 -print
}
執行shell函數
要執行函數,簡單地鍵入函數名即可
示例:
findit groups
/usr/bin/groups
/usr/local/backups/groups.bak
刪除shell函數
現在對函數做一些改動後,需要先刪除函數,使其對shell不 可用。使用unset命令完成刪除函數
命令格式爲:
unset function_name
示例:
unset findit
再鍵入set命令,函數將不再顯示
環境函數 使子進程也可使用
聲明:export -f function_name
查看:export -f 或 declare -xf
函數參數:
傳遞參數給函數:調用函數時,在函數名後面以空白分隔 給定參數列表即可;例如“testfunc arg1 arg2 ...”
在函數體中當中,可使用$1, $2, ...調用這些參數;還 可以使用$@, $*, $#等特殊變量
函數的返回值
return
0爲正確結果
1-255爲錯誤
unset取消函數定義,僅影響當前shell及其子Shell。
函數變量
1.本地函數、局部變量(本地變量)
local 變量名。本地函數中對變量的修改,賦值只在當前函數運行期間有效。
在函數中定義局部變量的方法
local NAME=VALUE
#!/bin/bash
func1 () { #定義函數func1
local name=test #定義一個本地函數
echo "func1:name=$name"
local age=18 #定義一個本地函數
echo "func1:age=$age"
echo $$ #查看當前進程
}
#輸出
root@(5-0)app# echo $$ #查看腳本執行前進程號
3541
root@(5-0)app# func1
func1:name=test
func1:age=18
3541 #腳本執行顯示的進程號
2.全局函數(本地變量)
不加local及declare -i(僅限整數數字) 默認爲全局函數,而declare -ig也爲全局函數(本地變量)。全局函數對變量的修改及賦值對當前shell均有效。但子Shell不繼承
func1 () { #定義函數
name=test
declare -i age=16
echo "func1:name=$name, func1:age=$age"
}
#輸出
root@(5-0)app# . lins #載入函數
root@(5-0)app# func1 #調用函數
func1:name=test, func1:age=16 #函數輸出結果
root@(5-0)app# echo $name #在當前bash中調用函數定義的變量
test #變量值
root@(5-0)app# bash - #切換子bash中
[root@localhost app]# echo $name #顯示變量
#沒有值
3.環境函數(環境變量)
declare -xf 環境函數中的變量會對子shell繼承 或 export -f
例題:
使用函數編寫腳本,實現打印國際象棋棋盤,要求每個格子由8個空格組成。
方法一:
#!/bin/bash
red() { #定義函數red輸出顯示樣式
echo -e "\033[41m \033[0m\c"
}
yel() { ##定義函數yel輸出顯示樣式
echo -e "\033[43m \033[0m\c"
}
redyel() { #定義函數redyel
for ((i=1;i<=4;i++));do #橫向進行循環4次
#因爲單個格子寬放大四倍,所以高度也要進行放大四倍
for ((j=1;j<=4;j++));do
#進行判斷輸出順序
[ "$1" = "-r" ] && { yel;red; } || { red;yel; }
done
echo
done
}
#每個棋盤8x8,定義縱向格子數量
for ((line=1;line<=8;line++));do
#進行奇偶判斷,來調用redyel函數
[ $[$line%2] -eq 0 ] && redyel || redyel -r # -r位置化參數
方法二:
#此方法使用for循環,不做詳解
#!/bin/bash
lvse (){
echo -e "\e[1;42m \e[0m\c"
}
huangse (){
echo -e "\e[1;43m \e[0m\c"
}
for i in {1..8};do
for x in {1..4};do
for n in {1..8};do
a=`echo $[$[$i+$n]%2]` #奇偶判斷
if [ $a -eq 0 ];then
huangse
else
lvse
fi
done
echo
done
done
函數遞歸:
函數直接或間接調用自身 (棋盤題第一種方法間接調用)
注意遞歸層數
遞歸實例:
階乘是基斯頓·卡曼於 1808 年發明的運算符號,是數學術語 一個正整數的階乘(factorial)是所有小於及等於該數的正整 數的積,並且有0的階乘爲1,自然數n的階乘寫作n!
n!=1×2×3×...×n
階乘亦可以遞歸方式定義:0!=1,n!=(n-1)!×n
n!=n(n-1)(n-2)...1
n(n-1)! = n(n-1)(n-2)!
#!/bin/bash
#定義函數fact
# fact() {
#進行判斷$!=0或等於1時輸出1
if [ $1 -eq 0 -o $1 -eq 1 ];then
echo 1
else
#否則,$1*$1-1,但是¥1-1不知道值,進行函數調用,得出$1-2的值,
#$1-2取不出來值,在進行函數調用,得出$1-3的值;以此類推。
#最終中所有值取出來進行乘法運算,取出乘積,$1的值就取出來了。
echo $[$1*$(fact $[$1-1])]
fi
}
fact $1
編寫函數,實現OS的版本判斷
編寫函數,實現取出當前系統eth0的IP地址
編寫函數,實現打印綠色OK和紅色FAILED
編寫函數,實現判斷是否無位置參數,如無參數,提示錯誤
#!/bin/bash
#定義OS的版本判斷函數:version
version () {
ver=$(cat /etc/redhat-release | sed -r "s/.* ([0-9])[.].*/\1/")
echo "當前版本號爲 $ver"
}
#定義取出當前系統eth0的IP地址的函數: ipaddr
ipaddr () {
ip=$(ip a s | grep ens33 | sed -n "2p"| cut -d/ -f1| awk '{print $2}')
echo $ip
}
#定義打印綠色OK和紅色FAILED的函數:print_color
print_color() {
echo -e "\033[41mFAILED\033[0m"
echo -e "\033[42mOK\033[0m"
}
#定義是否無位置參數,如無參數,提示錯誤的函數: judge_canshu
judge_canshu() {
if [ $# -eq 0 ];then
echo "請輸入至少一個參數"
else
echo "共有 $# 個參數"
fi
1、
在centos5或6上,編寫服務腳本/etc/init.d/routed,完成如下要求
(1) 腳本可接受參數:start, stop, restart, status
(2) 如果參數非此四者之一,提示使用格式後報錯退出
(3) 如是start:則創建/var/lock/subsys/routed, 並顯示“OK”
考慮:如果事先已經啓動過一次,則顯示routed already runing...
(4) 如是stop:則刪除/var/lock/subsys/SCRIPT_NAME, 並顯示“OK”
考慮:如果事先已然停止過了,則顯示"Faild"
(5) 如是restart,則先stop, 再start
考慮:如果本來沒有start,則在stop步驟顯示"Faild",而在start步驟顯示OK.
(6) 如是status, 則如果/var/lock/subsys/SCRIPT_NAME文件存在,則顯示“routed is running...”
如果/var/lock/subsys/SCRIPT_N AME文件不存在,則顯示“routed is stopped...”
(7)在所有模式下禁止啓動該服務,可用chkconfig 和 service命令管理
(8) 當start該腳本後,會在該主機上配置去往1.1.1.0和2.2.2.0網絡的路由去往172.18.0.1.
(9) 當stop該腳本後,會刪除(8)的兩條路由
(10) 當restart時,會先刪除路由,再加上.
#!/bin/bash
# chkconfig: 2345 19 80 #應用於那幾個模式 ,並設置開關機啓動順序
# description:routed #服務名稱
if ! [ -d /var/lock/subsys ];then
mkdir -p /var/lock/subsys &> /dev/null
fi
. /etc/init.d/functions
start () {
if [ -e /var/lock/subsys/routed ];then
action "Staring routed: routed already runing" false
else
touch /var/lock/subsys/routed
route add -net 1.1.1.0/24 gw 172.18.0.1
route add -net 2.2.2.0/24 gw 172.18.0.1
action "Starting routed:" true
fi
}
stop () {
if ! [ -e /var/lock/subsys/routed ];then
action "Shutting down routed:routed not running" false
else
rm -rf /var/lock/subsys/routed
route del -net 1.1.1.0/24 gw 172.18.0.1
route del -net 2.2.2.0/24 gw 172.18.0.1
action "Shutting down routed:" true
fi
}
status () {
if [ -e /var/lock/subsys/routed ];then
echo "routed is running..."
else
echo "routed is stopped..."
fi
}
case $1 in
start)
start;;
stop)
stop;;
restart)
stop
start;;
status)
status;;
*)
echo "Usage: $0 {start|stop|status|restart} "
esac
2、
編寫腳本/root/bin/copycmd.sh
(1) 提示用戶輸入一個可執行命令名稱
(2) 獲取此命令所依賴到的所有庫文件列表
(3) 複製命令至某目標目錄(例如/mnt/sysroot)下的對應路徑下; 如:/bin/bash ==> /mnt/sysroot/bin/bash
/usr/bin/passwd ==> /mnt/sysroot/usr/bin/passwd
(4) 複製此命令依賴到的所有庫文件至目標目錄下的對應路徑下: 如:/lib64/ld-linux-x86-64.so.2 ==> /mnt/sysroot/lib64/ld-linux-x86-64.so.2
(5)每次複製完成一個命令後,不要退出,而是提示用戶鍵入新的要複製的命令,並重復完成上述功能;直到用戶輸入quit退出
if ! [ $# -eq 2 ];then
echo "error, must two integer"
exit 0
elif
#判斷輸入的參數是不是數字
! [[ $1 =~ [0-9]+ ]] || ! [[ $2 =~ [0-9]+ ]];then
echo "error,$1 or $2 not a integer"
elif
#判斷兩個參數數值是否相等
[ $1 -eq $2 ];then
echo "$1 and $2 are equal"
elif
#兩個參數比較大小
[ $1 -lt $2 ];then
a=$2
echo "$a is larger"
else a=$1
echo "$a is larger"
fi
3.
斐波那契數列又稱黃金分割數列,因數學家列昂納多·斐波那契以兔子繁殖爲例子而引入,故又稱爲“兔子數列”,指的是這樣一個數列:0、1、1、2、3、5、8、13、21、34、……,斐波納契數列以如下被以遞歸的方法定義:F(0)=0,F(1)=1,F(n)=F(n-1)+F(n-2)(n≥2)
利用函數,求n階斐波那契數列
#!/bin/bash
shulie () { #定義函數shulie
if [ $1 -eq 1 ];then #判斷輸入數是否爲1,成立時輸出0
echo -n "0 "
elif [ $1 -eq 2 ];then #判斷輸入數是否爲2,成立時輸出1
echo -n "1 "
elif [ $1 -gt 2 ];then #判斷輸入數是否大於2,成立時,輸出n-1和n-2的和
#當n=3,先進行函數調用,計算出n-1和n-2的結果在進行加法運算,也就是先計算2和1的結果再相加
#當=8時,調用函數計算,7和6的值,7和6沒有給與值,又會再調用函數,可計算組成7的5、6和組成6的4、5,
#但是4、5、6的也沒有,所以他們也會再次調用函數計算出組成他們各自的n-1和n-2,以此類推,最後都計算出
#來了,纔會推出我們所要的n-1和n-2的值,想加就是我們要的n的值
echo -n "$[$(shulie $[$1-1])+$(shulie $[$1-2])] "
fi
}
a=$1
#做循環就可以將從零到n的各數列值分別列出
for i in $(seq 1 $a);do
shulie $i
done
echo
#調用函數shulie ,並賦值第一個位置變量
#shulie $a
4.漢諾塔(又稱河內塔)問題是源於印度一個古老傳說。大梵天創造世界的時候做了三根金剛石柱子,在一根柱子上從下往上按照大小順序摞着64片黃金圓盤。大梵天命令婆羅門把圓盤從下面開始按大小順序重新擺放在另一根柱子上。並且規定,在小圓盤上不能放大圓盤,在三根柱子之間一次只能移動一個圓盤,利用函數,實現N片盤的漢諾塔的移動步驟
#!/bin/bash
count=0
n_1()
{
let count++
echo "第${count}步:將${2}號圓盤從${1}移動到${3}"
}
n_2()
{
if [ $1 -eq 1 ];then
n_1 $2 1 $4
else
n_2 $[$1-1] $2 $4 $3
n_1 $2 $1 $4
n_2 $[$1-1] $3 $2 $4
fi
}
read -p "please input the number: " num
n_2 $num X Y Z
num=3
n_2 3 X Y Z
n_2 2 X Z Y
n_2 1 X Y Z
n_1 X 1 Z
輸出:第1步:將1由X移到Z
n_1 X 2 Y
輸出:第2步:將2由X移到Y
n_2 1 Z X Y
n_1 Z 1 Y
輸出:第3步:將1由Z移到Y
n_1 X 3 Z
輸出:第4步:將3由X移到Z
n_2 2 Y X Z
n_2 1 Y Z X
n_1 Y 1 X
輸出:第5步:將1由Y移動X
n_1 Y 2 Z
輸出:第6步:將2由Y移到Z
n_2 1 X Y Z
n_1 X 1 Z
輸出:第7步:將1由X移到Z