2012-10-10 55 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