Linux Shell腳本編程-函數


函數介紹
  定義:把一段獨立功能的的代碼當做一個整體,並為之一個名字,命名的代碼段,此即為函數;
  功能:函數function是由若干條shell命令組成的語句塊,實現代碼重用和模塊化編程。
  注意:定義函數的代碼段不會自動執行,在調用時執行;所謂函數調用,就在代碼中給定函數名稱即可;函數名出現的任何位置,在代碼執行時,都會被自動替換為函數代碼;

  函數和shell程序比較相似,區別在於:
  Shell程序在子Shell中運行,而函數在當前Shell中運行。因此在當前Shell中,函數可以對shell中變量進行修改;
  它與shell程序形式上是相似的,不同的是它不是一個單獨的進程,不能獨立運行,而是shell程序的一部分。

函數語法和使用
  語法:函數由兩部分組成函數名和函數體。
  語法一:
  function f_name {
  ...函數體...
 }
  語法二:
 f_name() {
  ...函數體...
  }

  函數的定義和使用:
  可在交互式環境下定義函數
  可將函數放在腳本文件中作為它的一部分(自己定義自己用)
  可放在只包含函數的單獨文件中
  函數調用:函數只有被調用才會執行
  調用方法:在代碼中給定函數名稱,函數名出現的地方,會被自動替換為函數代碼
  函數的生命周期:被調用時創建,返回時終止

函數的兩種返回值
  函數的執行結果返回值:使用echo或printf命令進行輸出;函數體中調用命令的輸出結果
  函數的退出狀態碼:其狀態返回結果為函數中執行的最后一條命令的狀態結果
  自定義退出狀態碼:return
  return [0-255] :從函數中返回,用最后狀態命令決定返回值
    0:成功,無錯誤返回
    1-255:失敗,有錯誤返回

示例:給定一個用戶名,顯示其用戶名,id和默認的shell
#!/bin/bash
#
userinfo() {
  if id $username &> /dev/null; then
    getent passwd $username| cut -d: -f1,3,7
  else
    echo "No such user"
fi
}

[ $# -lt 1 ] && echo "At least one username." && exit 2

username=$1
userinfo

username=$2
userinfo

示例:以函數的方式改寫服務框架腳本
#!/bin/bash
#
#chkconfig: - 50 50
#description: test service scipt
#

prong=$(basename $0)
lockfile=/var/lock/subsys/$prong

start() {
  if [ -f $lockfile ];then
    echo "$prong is running yet."
  else
    touch $lockfile
    [ $? -eq 0 ] && echo "start $prong finished."
  fi
}
stop() {
  if [ -f $lockfile ];then
    rm -f $lockfile
    [ $? -eq 0 ] && echo "stop $prong finished."
  else
    echo "$prong is not running."
  fi
}
status() {
  if [ -f $lockfile ];then
    echo "$prong is running"
  else
    echo "$prong is stopped"
  fi
}

usage() {
  echo "Usage: $prong {start|stop|restart|status}"
}

case $1 in
start)
  start
  ;;
stop)
  stop
  ;;
restart)
  stop
  start
  ;;
status)
  status
  ;;
*)
  usage
  exit 1
esac

函數定義及用法詳述
  函數可以定義在三個場景:交互環境下、腳本中 、單獨的函數文件
  1、交互環境下定義和使用函數:
  示例:鍵入函數名,注意左大括號和函數體之間需要有空格。這里要注意優先級,如果函數名和定義的別名、內部命令外部命令同名,優先級為:別名>函數>內部命令>外部命令;以該方法定義的函數將一直保留到用戶從系統退出,或執行了unset命令:
[root@centos7 function]# fun() { who; hostname ;}
[root@centos7 function]# fun
root pts/0 2017-03-02 09:39 (192.168.1.101)
centos7

  2、在腳本文件中定義和使用函數
  方法:函數在使用前必須定義,因此應將函數定義放在腳本開始部分,直至shell首次發現它后才能使用;調用函數僅使用其函數名即可。
  注意:在腳本中的函數在外面是用不了的,因為不在同一個shell中,運行腳本是開了一個子shell,而在外面是父shell。
示例:
#!/bin/bash
#

fun1() {
  echo "this is fun1"
}

echo "fun1 before"
fun1
echo "fun1 after"

# 執行結果如下:
[root@centos7 function]# bash test1.sh
fun1 before
this is fun1 # 函數只有在被調用才會執行
fun1 after


  3、使用函數文件定義函數
  可以將經常使用的函數存入函數文件,然后將函數文件載入shell。文件名可任意選取,但最好與相關任務有某種聯系。例如:functions.main
  一旦函數文件載入(source或.)shell,就可以在命令行或腳本中調用函數。可以使用set命令查看所有定義的函數,其輸出列表包括已經載入shell的所有函數。若要改動函數,首先用unset命令從shell中刪除函數。改動完畢后,再重新載入此文件

  函數文件載入shell的格式:
    . /PATH/TO/SOMEFILE或者source /PATH/TO/SOMEFILE

  查看是否載入:
    用source或 . 載入shell后,可使用set命令檢查函數是否已載入。set命令將在shell中顯示所有的載入函數
    
  刪除shell函數:
    現在對函數做一些改動。首先刪除函數,使其對shell不可用。使用unset命令完成此功能。
    命令格式為:unset function_name

  在腳本中載入函數:在腳本中使用函數,只要source FILES或者. FILE(存放函數的文件),就可以載入到當前shell當中,調用函數了。
 注意:
  1、Shell函數在當前Shell中運行。因此在當前Shell中,函數可以對shell中變量進行修改;
  2、當函數載入到當前環境中或腳本中時,當前環境或者腳本中定義的變量會和函數中定義的變量起沖突,這是因為,我們在當前環境中或者腳本中定義的為本地變量,函數載入當前shell之后,其實就是當前shell的一部分,所以定義的本地變量對其也有效。為了避免這種沖突,函數要使用局部變量。

示例:
1、首先創建一個函數文件,此函數文件中可以存放多個函數;
[root@centos7 function]# cat funs
#!/bin/bash
#

fun1() {
  echo "This is fun1"
}

fun2() {
  echo "This is fun2"
}
2、在當前shell中要想調用此函數,要使用 source 或者 . 加載到當前shell 中方可
# 直接調用,bash(相當於開了一個子shell)都不行
[root@centos7 ~]# fun1
-bash: fun1: 未找到命令

[root@centos7 ~]# bash fun1 # 相當於
bash: fun1: 沒有那個文件或目錄

# source 加載到當前shell
[root@centos7 ~]# source /root/bin/function/funs
[root@centos7 ~]# fun1
This is fun1
[root@centos7 ~]# fun2
This is fun2

# 刪除shell函數,再執行就沒有了
[root@centos7 ~]# unset fun1
[root@centos7 ~]# fun1
-bash: fun1: 未找到命令
[root@centos7 ~]# fun2
This is fun2
[root@centos7 ~]# unset fun2
[root@centos7 ~]# fun2
-bash: fun2: 未找到命令
3、在腳本中使用函數,只要source FILES或者. FILE(存放函數的文件),就可以載入到當前shell當中,調用函數了。
[root@centos7 function]# cat test2.sh
#!/bin/bash
#
source /root/bin/function/funs
fun1
echo "taotao"
fun2

# 執行結果如下:
[root@centos7 function]# fun1
-bash: fun1: 未找到命令 # 在當前shell中不能使用,因為腳本為子shell,當前環境為父shell
[root@centos7 function]# fun2
-bash: fun2: 未找到命令

[root@centos7 function]# bash test2.sh
This is fun1
taotao # 調用函數成功
This is fun2

函數參數:
  函數可以接受參數:
    傳遞參數給函數:調用函數時,在函數名后面以空白分隔給定參數列表即可;例如"testfunc arg1 arg2 ..."
    在函數體中當中,可使用$1, $2, ...調用這些參數;還可以使用$@, $*, $#等特殊變量

示例1:
1、添加10個用戶,(添加用戶的功能使用函數實現),用戶名作為參數傳遞給函數
[root@centos7 function]# cat useradd.sh
#!/bin/bash
#
addusers () {
  if id $1 &> /dev/null; then
    return 5
  else
    useradd $1
    retval=$? # 表示函數的狀態返回值,這里如果添加成功則為0
    return $retval #
  fi
}

for i in {1..10};do
  addusers ${1}${i} # $1 為腳本傳遞的參數變量,$i 為循環體中i的變量
  renum=$? # 這里的狀態返回值取決於函數中 return 的值,為0,表示添加成功,為5表示用戶存在
  if [[ "$renum" -eq 5 ]];then
    echo "user ${1}${i} exits."
  elif [[ "$renum" -eq 0 ]];then
    echo "Add user ${1}${i} successd."
  else
    echo "Unknow Error."
  fi
done

函數變量:
  變量的作用域:
  環境變量:當前shell和子shell有效
  本地變量:只在當前shell進程有效,為執行腳本會啟動專用子shell進程;因此本地變量的作用范圍是當前shell腳本程序文件,包括腳本中的函數
  局部變量:作用域是函數的生命周期;函數結束時變量被自動銷毀

  在函數中定義局部變量的方法:
  local NAME=VALUE

  注意:
  函數中也可調用本地變量,如果函數中定義的變量名稱和本地變量相同,函數調用之后會覆蓋本地變量的值;為了避免和本地變量相沖突,使之互不干擾,所以在函數中使用的變量要使用局部變量

示例:
1、在函數中定義的本地變量和腳本中定義的本地變量名相同,但值不同的執行結果;
[root@centos7 function]# cat test.sh
#!/bin/bash
#

setname() {
  name=jerry
  echo "Function:$name"
}

name=tom
echo "Fun before:$name"
setname
echo "Fun after:$name"

#執行結果,可以發現 函數中定義的變量覆蓋了腳本中定義的變量,
[root@centos7 function]# bash test.sh
Fun before:tom # 腳本執行前為本地變量
Function:jerry # 調用函數,函數也會調用本地變量,並再次給其賦值
Fun after:jerry # 腳本中定義的變量被函數中定義的變量覆蓋

2、在函數中定義局部變量,和腳本中定義本地變量但值不同的結果:
[root@centos7 function]# cat test.sh
#!/bin/bash
#

setname() {
  local name=jerry
  echo "Function:$name"
}

name=tom
echo "Fun before:$name"
setname
echo "Fun after:$name"

#執行結果如下:
[root@centos7 function]# bash test.sh
Fun before:tom
Function:jerry
Fun after:tom
結論:
  如果函數中有局部變量,如果其名稱同本地變量,使用局部變量。否則,函數執行之后會把當前shell中定義的相同的變量覆蓋。
  定義的局部變量只是在函數內部使用,互不干擾;如果不加local就是本地變量了。
函數遞歸:

函數遞歸:
  函數直接或間接調用自身
  遞歸實例(階乘):
  階乘是所有小於及等於該數的正整數的積,0的階乘為1,自然數n的階乘寫作n!。
  階乘亦可以遞歸方式定義:
    n! = n(n-1)! = n(n-1)(n-2)! = n(n-1)(n-2)(n-3)...1
    0! = 1
    
示例1:給定一個正整數,求其階乘:
[root@centos7 function]# cat jiecheng.sh
#!/bin/bash
#

fab() {
  if [ $1 -eq 0 -o $1 -eq 1 ];then
    echo 1
  else
    echo $[$1*$(fab $[$1-1])]
  fi
}

fab $1

#執行結果如下:
[root@centos7 function]# bash jiecheng.sh 2
2
[root@centos7 function]# bash jiecheng.sh 5
120
[root@centos7 function]# bash jiecheng.sh 3
6


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM