在Linux開發中,經常會撰寫shell腳本來執行一些任務,通常是一個腳本只做一件事,隨著任務的增加,腳本會越來越多,可復用的地方也會逐漸增加,這時就需要提取出腳本中的公共的功能放到一個通用的腳本中,其他腳本都能復用它

本篇文章介紹shell腳本中如何執行外部腳本,如何呼叫外部腳本中的函式,以及腳本復用相關的方法
執行外部腳本的方式
假如在當前目錄有 a.sh 腳本,內容如下
#!/bin/bashecho "a.sh..."
在一個腳本中執行外部腳本主要有以下幾種方式
- source 外部腳本名字
在當前目錄下的 b.sh 腳本,內容如下:
#!/bin/bashsource a.shecho "b.sh..."
執行 ./b.sh,結果如下
[root@ecs-centos-7 ~]# ./b.sh a.sh...b.sh...
腳本中 source a.sh 命令 會先執行當前目錄下的 a.sh腳本,所以結果會先輸出 a.sh...再輸出 b.sh腳本本身的列印
- 點號 外部腳本名字
把 b.sh 腳本中執行a.sh腳本的陳述句修改成 點號 + 空格 + a.sh ,修改之后的腳本內容如下:
注意:點號和a.sh之間一定要加上空格,否則執行的時候會出錯
#!/bin/bash. a.shecho "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/bashsh a.shecho "b.sh..."
執行 ./b.sh 命令, 結果如下
[root@ecs-centos-7 ~]# ./b.sh a.sh...b.sh...
可以看出,結果輸出和上面兩種方式是一樣的
三種方式的有什么區別
呼叫外部腳本有 source 外部腳本 、點號 外部腳本、sh 外部腳本 三種方式,它們之間有什么區別呢?
其中,source 外部腳本 和 點號 外部腳本 兩種方式是相同的,當前腳本繼承了外部腳本的全域變數和函式, 相當于把外部腳本的函式和全域變數匯入了當前腳本中
修改 a.sh 和 b.sh 腳本, 內容如下
a.sh腳本
#!/bin/bashVAR_A=10func_a(){ echo "a.sh...pid:$$,param:$1"}
b.sh腳本
#!/bin/bashsource a.sh func_a $1echo "vara:$VAR_A"echo "b.sh...pid:$$"
執行 ./b.sh 5 命令,結果如下
[root@ecs-centos-7 ~]# ./b.sh 5a.sh...pid:21485,param:5vara:10b.sh...pid:21485
兩個腳本中的 $$ 是指執行腳本的行程ID,從結果可以看出,a.sh 和 b.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.sh 中 source a.sh 修改成 . a.sh , 執行 ./b.sh 5, 結果依然是相同的
由于 sh 外部腳本的方式是當前腳本和外部腳本在兩個不同的行程中執行,所以當前腳本不能直接使用外部腳本中的函式和全域變數
修改 a.sh 和 b.sh 腳本, 內容如下
a.sh腳本
#!/bin/bashtest_a(){ echo "a.sh...test_a"}echo "a.sh...pid:$$"
b.sh腳本
#!/bin/bashsh a.shecho "b.sh...pid:$$"test_a
執行 ./b.sh 命令,結果如下
[root@ecs-centos-7 ~]# ./b.sh a.sh...pid:21818b.sh...pid:21817./b.sh:行7: test_a: 未找到命令
從結果可以看出,執行 a.sh 和 b.sh 的行程ID是不同的,b.sh腳本行程找不到test_a函式,所以在b.sh中呼叫test_a 函式會提示 未找到命令
呼叫外部腳本中的函式
上一節講到 sh 外部腳本 的方式無法直接使用外部腳本中函式和全域變數,下面提供幾種方法可以解決這個問題
- case 分支選擇
這種方法類似于程式代碼中的 switch case 陳述句,通過switch 選擇不同的分支從而執行不同的邏輯,shell腳本中是使用case關鍵字來實作的
a.sh腳本
#!/bin/bashVAR_A=10test_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/bashecho "b.sh...pid:$$"sh a.sh ta 3 5ret=$(sh a.sh var)echo "ret:$ret"
執行 ./b.sh 命令,結果如下
[root@ecs-centos-7 ~]# ./b.sh b.sh...pid:24813test_a..pid:24814,p1:3,p2:5ret:10
腳本b.sh一開始列印了呼叫自身的行程ID
sh a.sh ta 3 5 陳述句是呼叫a.sh腳本,傳入的三個引數分別是ta, 3, 5 ,執行a.sh時,傳入的第一個引數 ta經過case匹配之后呼叫 test_a函式,并把剩下的兩個引數 3和5作為引數傳入函式
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/bashVAR_A=10test_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/bashecho "b.sh...pid:$$"sh a.sh test_a 3 5ret=$(sh a.sh get_var)
執行 ./b.sh 命令,結果如下
[root@ecs-centos-7 ~]# ./b.sh b.sh...pid:25086test_a..pid:25087,p1:3,p2:5ret:10
可以看出,結果和上面 case 的方法是一樣的
現在其他腳本中都可以通過 sh a.sh 函式名 引數串列 這樣的方式呼叫 a.sh 腳本中的函式了,通過 $(sh a.sh 函式名 引數串列)的方式獲取 a.sh腳本函式的回傳值
- 兩者的優缺點
與case分支選擇的方式相比,函式呼叫模板的優點是呼叫者只需要關心復用的腳本中函式名、函式傳入引數、函式回傳值就可以直接使用
缺點是如果有多個腳本都呼叫了復用腳本中的函式,當復用腳本中函式名變更時,需要修改所有呼叫了它的地方
函式呼叫模板方式的缺點恰恰是case分支選擇方式的有點,case分支選擇的方式時根據傳入的字串引數呼叫不同的函式,這里的字串引數相當于函式的別名,只要這個引數保持不變,腳本中的函式名字可以任意變更
上述的優缺點比較只是一個相對的比較,實際應用中下不會很明顯,大部分情況兩種方式都可以使用
小結
在撰寫shell腳本的程序中,經常會遇到一些莫名奇妙的問題,有些問題就算撓破頭皮都不知道如何解決,腳本復用可以把一些公共功能提取出來,形成一個個的功能模塊,不僅有助于減少我們撰寫腳本時犯的錯誤,而且對后期的腳本維護很有幫助
以上就是良許教程網為各位朋友分享的Linux下如何復用外部shell腳本,
本文由博客一文多發平臺 OpenWrite 發布!
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/255439.html
標籤:其他
下一篇:STM32 Flash詳解
