2012-10-10 89 views
31

我有一個我想用shUnit測試的shell腳本。腳本(和所有功能)都在一個文件中,因爲它使安裝變得更加容易。從shell腳本導入函數

示例script.sh

#!/bin/sh 

foo() { ... } 
bar() { ... } 

code 

我想寫的第二文件(這並不需要被分發和安裝)來測試在script.sh定義的函數

喜歡的東西run_tests.sh

#!/bin/sh 

. script.sh 

# Unit tests 

現在問題出在.(或source在Bash)。它不僅解析函數定義,而且還執行腳本中的代碼。

由於不帶參數的腳本做什麼不好我可以

. script.sh > /dev/null 2>&1 

,但我徘徊,如果有更好的方式來實現我的目標。

編輯

我提出的解決方法不在的情況下工作,被執行的腳本調用exit所以我必須陷阱出口

#!/bin/sh 

trap run_tests ERR EXIT 

run_tests() { 
    ... 
} 

. script.sh 

run_tests函數被調用,但只要我重定向源命令的輸出腳本中的功能不會被解析,並且在陷阱處理程序中不可用

這可行,但我得到了outp的script.sh UT:

#!/bin/sh 
trap run_tests ERR EXIT 
run_tests() { 
    function_defined_in_script_sh 
} 
. script.sh 

這不打印輸出,但我得到的功能沒有定義的錯誤:

#!/bin/sh 
trap run_tests ERR EXIT 
run_tests() { 
    function_defined_in_script_sh 
} 
. script.sh | grep OUTPUT_THAT_DOES_NOT_EXISTS 

這不打印輸出和run_tests陷阱處理程序不叫在所有:

#!/bin/sh 
trap run_tests ERR EXIT 
run_tests() { 
    function_defined_in_script_sh 
} 
. script.sh > /dev/null 

回答

39

按照bash manpage的「shell內建命令」部分,.又名source採用傳遞給正在採購的腳本的可選參數列表。你可以用它來介紹一個無所事事的選擇。例如,script.sh可能是:

#!/bin/sh 

foo() { 
    echo foo $1 
} 

main() { 
    foo 1 
    foo 2 
} 

if [ "${1}" != "--source-only" ]; then 
    main "${@}" 
fi 

unit.sh可能是:

#!/bin/bash 

. ./script.sh --source-only 

foo 3 

然後script.sh將正常和unit.sh將有機會獲得所有功能從script.sh但不會調用main()碼。

注意,額外的參數給source不是POSIX,所以/bin/sh可能不會在unit.sh開始處理它,因此#!/bin/bash

+1

到'shift'參數列表所以'main'不會取得聯繫與'--source您可能希望只有' –

+0

@HubertGrzeskowiak好點,固定。感謝您的建議! – andrewdotn

+1

'shift'在那裏沒有意義,因爲'main'只在'--source-only'不是**第一個參數時運行。 –

16

從Python的拿起這種技術,但概念作品在bash或任何其他殼就好了......

的想法是,我們把腳本的主要代碼段成一個函數。然後,在腳本的最後,我們放置了一個'if'語句,如果我們執行了腳本,它將只調用該函數,但如果我們提供它,則不會。然後,我們從我們的'runtests'腳本中明確地調用了腳本()函數,該腳本來源於'腳本'腳本,因此包含了它的所有功能。

這依賴於一個事實,如果我們源腳本,在bash維護環境變量$0,這是腳本正在執行的名字,將成爲調用(父)腳本的名稱(runtests在這種情況),而不是源腳本。

(我已經改名爲script.sh只是script引起.sh是多餘的,混淆了我。:-)

下面是兩個腳本。一些注意事項...

  • [email protected]評估所有的傳遞給函數或 腳本作爲單獨的字符串參數。相反,如果我們使用$*,則所有 參數將連接在一起成爲一個字符串。
  • RUNNING="$(basename $0)"是必需的,因爲$0始終至少包含 ,如./script中的當前目錄前綴。試驗if [[ "$RUNNING" == "script" ]]...。是僅當script直接從命令行運行 才導致 script調用腳本()函數的魔法。

腳本

#!/bin/bash 

foo() { echo "foo()"; } 

bar() { echo "bar()"; } 

script() { 
    ARG1=$1 
    ARG2=$2 
    # 
    echo "Running '$RUNNING'..." 
    echo "script() - all args: [email protected]" 
    echo "script() -  ARG1: $ARG1" 
    echo "script() -  ARG2: $ARG2" 
    # 
    foo 
    bar 
} 

RUNNING="$(basename $0)" 

if [[ "$RUNNING" == "script" ]] 
then 
    script "[email protected]" 
fi 

runtests

#!/bin/bash 

source script 

# execute 'script' function in sourced file 'script' 
script arg1 arg2 arg3 
+1

這有兩個缺點:無論何時重命名腳本,都必須更改其內容;它不符合符號鏈接(不確定別名)。 –

3

如果你正在使用bash,類似的解決方案,以@ andrewdotn的做法(但無需額外的標誌或視腳本名稱)可以通過使用BASH_SOURCE數組來完成。

script.sh:

#!/bin/bash 

foo() { ... } 
bar() { ... } 

main() { 
    code 
} 

if [[ "${#BASH_SOURCE[@]}" -eq 1 ]]; then 
    main "[email protected]" 
fi 

run_tests.sh:

#!/bin/bash 

. script.sh 

# Unit tests