从shell脚本导入函数
我有一个shell脚本,我想用它进行测试。脚本(和所有函数)都在一个文件中,因为它使安装更加容易从shell脚本导入函数,shell,Shell,我有一个shell脚本,我想用它进行测试。脚本(和所有函数)都在一个文件中,因为它使安装更加容易 script.sh的示例 #!/bin/sh foo () { ... } bar () { ... } code #!/bin/sh . script.sh # Unit tests 我想编写第二个文件(不需要分发和安装)来测试script.sh #!/bin/sh foo () { ... } bar () { ... } code #!/bin/sh . script.sh
script.sh的示例
#!/bin/sh
foo () { ... }
bar () { ... }
code
#!/bin/sh
. script.sh
# Unit tests
我想编写第二个文件(不需要分发和安装)来测试script.sh
#!/bin/sh
foo () { ... }
bar () { ... }
code
#!/bin/sh
. script.sh
# Unit tests
类似于run\u tests.sh的内容
#!/bin/sh
foo () { ... }
bar () { ... }
code
#!/bin/sh
. script.sh
# Unit tests
现在问题在于Bash中的
(或源代码)。它不仅解析函数定义,还执行脚本中的代码
因为没有参数的脚本没有什么不好的,所以我可以
. script.sh > /dev/null 2>&1
但我在想是否有更好的方法来实现我的目标
编辑
#!/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
在源代码脚本调用exit
的情况下,我建议的解决方案不起作用,因此我必须捕获退出
#!/bin/sh
trap run_tests ERR EXIT
run_tests() {
...
}
. script.sh
调用run\u tests
函数,但一旦重定向源命令的输出,脚本中的函数就不会被解析,并且在陷阱处理程序中不可用
这是可行的,但我得到了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\u tests
trap处理程序:
#!/bin/sh
trap run_tests ERR EXIT
run_tests() {
function_defined_in_script_sh
}
. script.sh > /dev/null
根据中的“Shell内置命令”部分,
akasource
获取一个可选的参数列表,这些参数被传递给正在获取源代码的脚本。你可以用它来引入一个什么都不做的选项。例如,script.sh
可以是:
#!/bin/sh
foo() {
echo foo $1
}
main() {
foo 1
foo 2
}
if [ "${1}" != "--source-only" ]; then
main "${@}"
fi
#!/bin/bash
. ./script.sh --source-only
foo 3
和unit.sh
可以是:
#!/bin/sh
foo() {
echo foo $1
}
main() {
foo 1
foo 2
}
if [ "${1}" != "--source-only" ]; then
main "${@}"
fi
#!/bin/bash
. ./script.sh --source-only
foo 3
然后script.sh
将正常运行,unit.sh
将可以访问script.sh
中的所有函数,但不会调用main()
代码
请注意,source
的额外参数不在POSIX中,因此/bin/sh
可能无法处理它,因此#/bin/bash
在unit.sh
的开头,从Python中获得了这项技术,但是这个概念在bash或任何其他shell中都可以正常工作
我们的想法是将脚本的主代码部分转换为函数。然后在脚本的最后,我们放置了一个“if”语句,该语句仅在执行脚本时调用该函数,而在源代码时不调用该函数。然后,我们显式地从“runtests”脚本调用script()函数,该脚本源于“script”脚本,因此包含其所有函数
这取决于这样一个事实:如果我们为脚本提供源代码,bash维护的环境变量$0
,即正在执行的脚本的名称,将是调用的(父)脚本的名称(runtests
,在本例中),而不是源代码脚本的名称
(我已将script.sh
重命名为script
,因为.sh
是多余的,让我感到困惑。:-)
下面是两个脚本。一些注释
$@
计算传递给函数的所有参数,或
脚本作为单个字符串。如果改为使用$*
,则所有
参数将被连接到一个字符串中
RUNNING=“$(basename$0)”
是必需的,因为$0
始终包括在
至少使用/script
中的当前目录前缀
- 测试
如果[[“$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
运行测试
#!/bin/bash
source script
# execute 'script' function in sourced file 'script'
script arg1 arg2 arg3
如果您使用的是Bash,则可以通过使用Bash\u SOURCE
array来实现与@andrewdotn方法类似的解决方案(但不需要额外的标志或取决于脚本名称)
script.sh:
#!/bin/bash
foo () { ... }
bar () { ... }
main() {
code
}
if [[ "${#BASH_SOURCE[@]}" -eq 1 ]]; then
main "$@"
fi
运行_tests.sh:
#!/bin/bash
. script.sh
# Unit tests
如果您使用的是Bash,另一种解决方案可能是:
#!/bin/bash
foo () { ... }
bar () { ... }
[[ "${FUNCNAME[0]}" == "source" ]] && return
code
这是我设计的。假设我们的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
~这有两个缺点:无论何时重命名脚本,都必须更改其内容;而且它不适用于符号链接(不确定别名)。如果从命令行执行,它会抛出一个错误:basename:非法选项--b
。您可能希望移动参数列表,以便main
不与--仅源代码
@HubertGrzeskowiak接触良好点,已修复。谢谢你的建议!shift
在这里没有意义,因为只有当--source only
不是第一个参数时,main
才会运行。@HelderPereira说得好!谢谢你的报道,真是太好了!我会亲自将测试条件交换到(${BASH#u SOURCE[@]}==1))
;短条件(${BASH_SOURCE[@]}==1))和&main“$@”
也应该可以工作,如果您不想让它做任何事情或者,它就足够了。