我有一个shell脚本,我想用shUnit测试。脚本(和所有功能)都在一个文件中,因为它使安装更容易。
script.sh
#!/bin/sh
foo () { ... }
bar () { ... }
code
我想编写第二个文件(不需要分发和安装)来测试script.sh
中定义的函数
像run_tests.sh
#!/bin/sh
. script.sh
# Unit tests
现在问题在于.
(或Bash中的source
)。它不仅解析函数定义,还执行脚本中的代码。
由于没有参数的脚本没有任何坏处,我可以
. script.sh > /dev/null 2>&1
但如果有更好的方法来实现我的目标,我就会徘徊。
修改
我建议的解决方法在源脚本调用exit
的情况下不起作用,所以我必须陷阱退出
#!/bin/sh
trap run_tests ERR EXIT
run_tests() {
...
}
. script.sh
调用run_tests
函数但是只要我重定向source命令的输出,脚本中的函数就不会被解析,并且在陷阱处理程序中不可用
这有效,但我得到script.sh
的输出:
#!/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
答案 0 :(得分:54)
根据bash manpage的“Shell Builtin命令”部分,.
又名source
采用传递给源脚本的可选参数列表。你可以使用它来引入一个do-nothing选项。例如,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
可能无法处理它 - 因此#!/bin/bash
开头为unit.sh
。
答案 1 :(得分:19)
从Python中选择了这种技术,但这个概念在bash或任何其他shell中运行得很好......
我们的想法是将脚本的主代码部分转换为函数。然后在脚本的最后,我们放置一个'if'语句,只有在我们执行脚本时才调用该函数,但如果我们采购它则不会。然后我们从'runtests'脚本中显式调用script()函数,该脚本源自'script'脚本,因此包含其所有函数。
这依赖于以下事实:如果我们获取脚本,那么bash维护的环境变量$0
(正在执行的脚本的名称)将是调用的名称(父)脚本(本例中为runtests
),而不是源脚本。
(我已将script.sh
重命名为script
,因为.sh
是多余的,让我感到困惑。: - )
以下是两个脚本。一些笔记......
$@
计算传递给函数或的所有参数
脚本作为单个字符串。相反,我们使用$*
,所有
参数将连接成一个字符串。RUNNING="$(basename $0)"
是必需的,因为$0
始终包含at
至少是./script
中的当前目录前缀。if [[ "$RUNNING" == "script" ]]...
。是导致的魔力
script
仅在直接运行script
时调用script()函数
来自命令行。<强>脚本强>
#!/bin/bash
foo () { echo "foo()"; }
bar () { echo "bar()"; }
script () {
ARG1=$1
ARG2=$2
#
echo "Running '$RUNNING'..."
echo "script() - all args: $@"
echo "script() - ARG1: $ARG1"
echo "script() - ARG2: $ARG2"
#
foo
bar
}
RUNNING="$(basename $0)"
if [[ "$RUNNING" == "script" ]]
then
script "$@"
fi
<强> runtests 强>
#!/bin/bash
source script
# execute 'script' function in sourced file 'script'
script arg1 arg2 arg3
答案 2 :(得分:5)
如果您正在使用Bash,可以使用BASH_SOURCE
数组来完成@andrewdotn方法的类似解决方案(但不需要额外的标记或取决于脚本名称)。
script.sh:
#!/bin/bash
foo () { ... }
bar () { ... }
main() {
code
}
if [[ "${#BASH_SOURCE[@]}" -eq 1 ]]; then
main "$@"
fi
run_tests.sh:
#!/bin/bash
. script.sh
# Unit tests
答案 3 :(得分:0)
如果您使用的是Bash,则另一种解决方案可能是:
#!/bin/bash
foo () { ... }
bar () { ... }
[[ "${FUNCNAME[0]}" == "source" ]] && return
code
答案 4 :(得分:0)
我设计了这个。假设我们的 shell 库文件是以下文件,名为 aLib.sh:
funcs=("a" "b" "c") # File's functions' names
for((i=0;i<${#funcs[@]};i++)); # Avoid function collision with existing
do
declare -f "${funcs[$i]}" >/dev/null
[ $? -eq 0 ] && echo "!!ATTENTION!! ${funcs[$i]} is already sourced"
done
function a(){
echo function a
}
function b(){
echo function b
}
function c(){
echo function c
}
if [ "$1" == "--source-specific" ]; # Source only specific given as arg
then
for((i=0;i<${#funcs[@]};i++));
do
for((j=2;j<=$#;j++));
do
anArg=$(eval 'echo ${'$j'}')
test "${funcs[$i]}" == "$anArg" && continue 2
done
unset ${funcs[$i]}
done
fi
unset i j funcs
一开始它会检查并警告检测到的任何函数名称冲突。 最后,bash 已经获取了所有函数,因此它会释放这些函数的内存并只保留被选中的函数。
可以这样使用:
user@pc:~$ source aLib.sh --source-specific a c
user@pc:~$ a; b; c
function a
bash: b: command not found
function c
~