如何複用外部shell腳本


在Linux開發中,常常會編寫shell腳原本執行一些任務,一般是一個腳本只作一件事,隨着任務的增長,腳本會愈來愈多,可複用的地方也會逐漸增長,這時就須要提取出腳本中的公共的功能放到一個通用的腳本中,其餘腳本都能複用它shell

本篇文章介紹shell腳本中如何執行外部腳本,如何調用外部腳本中的函數,以及腳本複用相關的方法centos

執行外部腳本的方式

假如在當前目錄有 a.sh 腳本,內容以下bash

#!/bin/bash

echo "a.sh..."

在一個腳本中執行外部腳本主要有如下幾種方式函數

  • source 外部腳本名字

在當前目錄下的 b.sh 腳本,內容以下:調試

#!/bin/bash

source a.sh
echo "b.sh..."

執行 ./b.sh,結果以下日誌

[root@ecs-centos-7 ~]# ./b.sh 
a.sh...
b.sh...

腳本中 source a.sh 命令 會先執行當前目錄下的 a.sh腳本,因此結果會先輸出 a.sh...再輸出 b.sh腳本自己的打印code

  • 點號 外部腳本名字

b.sh 腳本中執行a.sh腳本的語句修改爲 點號 + 空格 + a.sh ,修改以後的腳本內容以下:blog

注意:點號和a.sh之間必定要加上空格,不然執行的時候會出錯繼承

#!/bin/bash

. a.sh
echo "b.sh..."

執行 ./b.sh,結果以下進程

[root@ecs-centos-7 ~]# ./b.sh 
a.sh...
b.sh...

在上述腳本中, . a.sh 會先執行a.sh腳本, 結果會先輸出 a.sh...再輸出 b.sh...

  • sh 外部腳本名字

sh 外部腳本名字./外部腳本名字 兩種方式是同樣的,選擇哪種方式都沒問題,下面是之前面一種方式爲例說明的

b.sh 腳本中 source a.sh修改爲 sh a.sh ,修改以後的腳本內容以下:

#!/bin/bash

sh a.sh
echo "b.sh..."

執行 ./b.sh 命令, 結果以下

[root@ecs-centos-7 ~]# ./b.sh 
a.sh...
b.sh...

能夠看出,結果輸出和上面兩種方式是同樣的

三種方式的有什麼區別

調用外部腳本有 source 外部腳本點號 外部腳本sh 外部腳本 三種方式,它們之間有什麼區別呢?

其中,source 外部腳本點號 外部腳本 兩種方式是相同的,當前腳本繼承了外部腳本的全局變量和函數, 至關於把外部腳本的函數和全局變量導入了當前腳本中

修改 a.shb.sh 腳本, 內容以下

a.sh腳本

#!/bin/bash

VAR_A=10

func_a()
{
  echo "a.sh...pid:$$,param:$1"
}

b.sh腳本

#!/bin/bash

source a.sh 

func_a $1
echo "vara:$VAR_A"
echo "b.sh...pid:$$"

執行 ./b.sh 5 命令,結果以下

[root@ecs-centos-7 ~]# ./b.sh 5
a.sh...pid:21485,param:5
vara:10
b.sh...pid:21485

兩個腳本中的 $$ 是指執行腳本的進程ID,從結果能夠看出,a.shb.sh 都是在同一個進程內執行的,因此在 b.sh 腳本中執行 source a.sh 命令,會把 a.sh 腳本中的全局變量 VAR_A 和函數 func_a導入到 b.sh

b.sh中打印變量 VAR_A,輸出的值和 a.sh中相同,調用 func_a函數,輸出也說明了調用的是 a.sh中的函數

source 外部腳本點號 外部腳本 兩種方式是相同的, 因此, 把 b.shsource a.sh 修改爲 . a.sh , 執行 ./b.sh 5, 結果依然是相同的

因爲 sh 外部腳本的方式是當前腳本和外部腳本在兩個不一樣的進程中執行,因此當前腳本不能直接使用外部腳本中的函數和全局變量

修改 a.shb.sh 腳本, 內容以下

a.sh腳本

#!/bin/bash

test_a()
{
  echo "a.sh...test_a"
}

echo "a.sh...pid:$$"

b.sh腳本

#!/bin/bash

sh a.sh

echo "b.sh...pid:$$"

test_a

執行 ./b.sh 命令,結果以下

[root@ecs-centos-7 ~]# ./b.sh 
a.sh...pid:21818
b.sh...pid:21817
./b.sh:行7: test_a: 未找到命令

從結果能夠看出,執行 a.shb.sh 的進程ID是不一樣的,b.sh腳本進程找不到test_a函數,因此在b.sh中調用test_a 函數會提示 未找到命令

調用外部腳本中的函數

上一節講到 sh 外部腳本 的方式沒法直接使用外部腳本中函數和全局變量,下面提供幾種方法能夠解決這個問題

  • case 分支選擇

這種方法相似於程序代碼中的 switch case 語句,經過switch 選擇不一樣的分支從而執行不一樣的邏輯,shell腳本中是使用case關鍵字來實現的

a.sh腳本

#!/bin/bash

VAR_A=10

test_a()
{
   echo "test_a..pid:$$,p1:$1,p2:$2"
}
get_var()
{
  echo ${VAR_A}
}

case "$1" in
    ta)
      test_a $2 $3
      ;;
    var)
      get_var
      ;;
   *)
      echo "parameter err..."
esac

b.sh腳本

#!/bin/bash

echo "b.sh...pid:$$"

sh a.sh ta 3 5

ret=$(sh a.sh var)

echo "ret:$ret"

執行 ./b.sh 命令,結果以下

[root@ecs-centos-7 ~]# ./b.sh 
b.sh...pid:24813
test_a..pid:24814,p1:3,p2:5
ret:10

腳本b.sh一開始打印了調用自身的進程ID

sh a.sh ta 3 5 語句是調用a.sh腳本,傳入的三個參數分別是ta, 3, 5 ,執行a.sh時,傳入的第一個參數 ta 通過case匹配以後調用 test_a函數,並把剩下的兩個參數 35做爲參數傳入函數

ret=$(sh a.sh var) 語句時調用a.sh腳本,傳入一個var 參數,通過case匹配以後調用get_var函數,該函數的做用輸出腳本中全局變量VAR_A的值,語句中$()的做用是獲取()中命令的返回值,這裏是把a.sh腳本中 get_var函數的返回值賦值給 ret變量,因此該變量的值是 a.sh腳本中全局變量VAR_A的值

說明:若是想要獲取函數的返回值,能夠在函數中用 echo 打印相應的輸出值,而後使用$(函數名 參數列表)能夠獲取到函數中打印的值,如上面b.sh腳本中 ret=$(sh a.sh var)語句,變量ret的值是 a.sh腳本中 get_var函數輸出的值10

這裏須要注意的是, 若是函數中有echo調試日誌,那麼調試日誌也會一塊兒返回

  • 函數調用模板

上面介紹的用 case 關鍵字去匹配調用不一樣的函數有一個缺點,每次a.sh腳本中增長一個函數的時候,case 就須要添加一個分支,分支裏調用不一樣的函數,還須要注意函數是否有參數傳入以及參數數量是否正確

咱們能夠在每一個供外部調用腳本的尾部加上如下的語句,就能夠解決上述問題, 具體語句以下

if [ $# -ge 1 ]; then
   name="$1"
   shift 1
   $name "$@"
fi

上述語句首先判斷調用腳本時傳入的參數數量,只有參數數量大於等於1纔有效,傳入的第一個參數表示函數名字,從第二個參數到最後一個參數都會做爲參數傳入到函數中

這裏的 shift 1 是把傳入腳本的參數左移一個位置,好比:傳入腳本參數有 $1 $2 $3三個參數,左移一個位置以後, $2 移動到 $1 的位置,$3 移動到 $2 的位置,參數數量變爲2了

緣由: 傳入腳本的參數中,第一個參數是函數名字,從第二個參數起纔是函數的參數,若是不作左移處理,第一個參數函數名字也會做爲參數傳入到函數中

下面是完整的腳本內容

a.sh腳本

#!/bin/bash

VAR_A=10

test_a()
{
   echo "test_a..pid:$$,p1:$1,p2:$2"
}

get_var()
{
  echo ${VAR_A}
}

if [ $# -ge 1 ]; then
   name="$1"
   shift 1
   $name "$@"
fi

b.sh腳本

#!/bin/bash

echo "b.sh...pid:$$"

sh a.sh test_a 3 5

ret=$(sh a.sh get_var)

執行 ./b.sh 命令,結果以下

[root@ecs-centos-7 ~]# ./b.sh 
b.sh...pid:25086
test_a..pid:25087,p1:3,p2:5
ret:10

能夠看出,結果和上面 case 的方法是同樣的

如今其餘腳本中均可以經過 sh a.sh 函數名 參數列表 這樣的方式調用 a.sh 腳本中的函數了,經過 $(sh a.sh 函數名 參數列表)的方式獲取 a.sh腳本函數的返回值

  • 二者的優缺點

與case分支選擇的方式相比,函數調用模板的優勢是調用者只須要關心複用的腳本中函數名、函數傳入參數、函數返回值就能夠直接使用

缺點是若是有多個腳本都調用了複用腳本中的函數,當複用腳本中函數名變動時,須要修改全部調用了它的地方

函數調用模板方式的缺點偏偏是case分支選擇方式的有點,case分支選擇的方式時根據傳入的字符串參數調用不一樣的函數,這裏的字符串參數至關於函數的別名,只要這個參數保持不變,腳本中的函數名字能夠任意變動

上述的優缺點比較只是一個相對的比較,實際應用中下不會很明顯,大部分狀況兩種方式均可以使用

小結

在編寫shell腳本的過程當中,常常會遇到一些莫名奇妙的問題,有些問題就算撓破頭皮都不知道如何解決,腳本複用能夠把一些公共功能提取出來,造成一個個的功能模塊,不只有助於減小咱們編寫腳本時犯的錯誤,並且對後期的腳本維護頗有幫助

相關文章
相關標籤/搜索