我有一个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
函数被调用,但一旦重定向源命令的输出,脚本中的函数就不会被解析,并且在陷阱处理程序中也不可用
这是可行的,但我得到了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
6条答案
按热度按时间rxztt3cl1#
根据bash manpage的“Shell Builtin Commands”一节,
.
也就是source
接受一个可选的参数列表,这些参数被传递给脚本源。你可以用它来引入一个什么都不做的选项。例如,script.sh
可以是:unit.sh
可以是:然后
script.sh
将正常运行,unit.sh
将访问script.sh
的所有函数,但不会调用main()
代码。请注意,
source
的额外参数不在POSIX中,所以/bin/sh
可能无法处理它-因此unit.sh
的开头有#!/bin/bash
。hfwmuf9z2#
这是从Python中学习到的,但这个概念在bash或任何其他shell中都能很好地工作。
我们的想法是把脚本的主代码部分转换成一个函数。然后,在脚本的最后,我们放了一个'if'语句,它只会在执行脚本时调用该函数,而不是在源代码时调用该函数。然后,我们从'runtests'脚本中显式调用script()函数,该脚本包含了'script'脚本的来源,因此包含了它的所有函数。
这依赖于这样一个事实,即如果我们源脚本,bash维护的环境变量
$0
,它是正在执行的脚本的名称,将是调用(父)脚本的名称(在本例中是runtests
),而不是源脚本的名称。(我将
script.sh
重命名为script
,因为.sh
是多余的,让我困惑。:—)下面是两个剧本。一些笔记...
$@
计算为作为单个字符串传递给函数或脚本的所有参数。如果我们使用$*
,那么所有的参数都将连接到一个字符串中。RUNNING="$(basename $0)"
是必需的,因为$0
总是至少包含当前目录前缀,就像./script
中一样。if [[ "$RUNNING" == "script" ]]...
。只有当script
直接从命令行运行时,script
才会调用script()函数。脚本
运行测试
x6492ojm3#
如果您使用Bash,可以使用
BASH_SOURCE
数组来实现与@andrewdotn方法类似的解决方案(但不需要额外的标志或依赖于脚本名称)。script.sh:
run_tests.sh:
ycggw6v24#
现在问题出在
.
(或Bash中的source
)上。它不仅解析函数定义,还执行脚本中的代码。我喜欢让它看起来比这里的其他答案更像Python。我做了一点额外的,这样我就可以结束与
if [ "$__name__" = "__main__" ]; then
线。也可以在这里看到我的回答:What is the bash equivalent to Python'sif __name__ == '__main__'
?,这里我描述了:让我们更进一步,我将展示一个完整的库示例和多种导入它的方法:
详细示例:如何在Bash中编写、导入、使用和测试库?
这是我多年来编写和使用Bash库的一个非常漂亮的、几乎类似Python的风格。Bash是一种漂亮的“胶水”类型的语言,它允许您轻松地将多种语言的可执行文件绑定在一起。考虑到Bash已经存在了多久,我不知道为什么下面的风格没有更受欢迎,但也许它以前没有被想到或使用过这种方式。所以,开始吧。我想你会发现它真的很有用。
您还可以看到我在eRCaGuy_hello_world仓库中的hello_world_best.sh文件中用于所有bash脚本的一般起点。
您可以在floating_point_math.sh中看到一个完整的库示例。
library_basic_example.sh:
运行库以运行其单元测试
现在,使文件可执行。运行它将运行其内部单元测试:
运行命令和输出示例:
导入(源)库
要导入一个Bash库,您可以使用
source
或.
(更好)命令来“源”它。在这里阅读更多关于我的答案:source
(.
) vsexport
(and also some file lock [flock
] stuff at the end)。1.使用手动设置导入路径
您可以直接在bash终端中执行此操作,也可以在自己的bash脚本中执行此操作。现在就在你自己的终端里试试吧!:
一旦你源(导入)了Bash库,你就可以直接调用它的函数。下面是一个完整的运行和输出示例,显示一旦我获取(导入)了这个Bash库,我就可以在我的终端中执行
my_func1
、my_func2
等函数调用!:2.使用
BASHLIBS
环境变量,使导入Bash库更容易你可以从 * 任意路径 * 获取bash库。但是,环境变量,例如
BASHLIBS
,使它更容易。将此添加到~/.bashrc
文件的底部:现在,您可以将您的Bash库符号链接到该目录中,如下所示:
现在,
library_basic_example.sh
文件的符号链接存储在~/libs_bash/libraries/
中,并且BASHLIBS
环境变量已经设置并且export
绑定到我的环境中,我可以将我的库导入到任何Bash终端或我在终端中运行的脚本中,如下所示:3.【我最常用的技巧!]使用相对导入路径
这真的很漂亮很有力量。看这个!
假设您有以下目录布局:
上面的表示可以很容易地存在于一个大型程序或工具链中,你已经建立了一个分散的可运行脚本和库,特别是当你的各种bash脚本之间共享(采购/导入)代码时,无论它们位于哪里。
假设你有这些限制/要求:
1.上面的整个目录结构都存储在一个GitHub仓库中。
1.你需要任何人能够
git clone
这个仓库,只是有所有的脚本和导入和东西 * 神奇的工作 * 每个人!1.这意味着在将bash脚本相互导入时需要使用 relative imports。
1.例如,您将运行
my_script.sh
。1.您必须能够从任何地方调用
my_script.sh
*,这意味着:当你调用这个脚本运行时,你应该能够被cd
艾德到你整个文件系统中的 any 目录。my_script.sh
必须使用相对导入来导入my_lib_1.sh
、my_lib_2.sh
和my_lib_3.sh
。基本上,我们要找到正在运行的脚本的路径 *,然后使用该路径作为脚本周围其他脚本的相对起点!
阅读我的完整答案以了解更多细节:How to obtain the full file path, full directory, and base filename of any script being run OR sourced...even when the called script is called from within another bash function or script, or when nested sourcing is being used!。
完整示例:
my_script.sh:
就是这样!如果你知道命令的话超级简单!
Python能做到吗?不,至少不是天生的。在这方面,Bash比Python容易得多!Python在
import
时不直接使用文件系统路径。是much more complicated and convoluted than that但是,我正在开发一个import_helper.py
Python模块,以使这种类型的事情在Python中也很容易。我很快也会出版的。更进一步
1.在我的eRCaGuy_hello_world存储库中查看我的完整和真正有用的Bash浮点库:floating_point_math.sh。
1.请参阅我的自述文件中关于Bash库安装和使用的一些替代说明:https://github.com/ElectricRCAircraftGuy/eRCaGuy_hello_world/tree/master/bash/libraries
参见
assert.sh
Bash repo,它看起来对更健壮的Bash单元测试非常有用!注意:我迁移了这个答案from here,在那里我不小心创建了一个重复的Q&A。
unguejic5#
如果您正在使用Bash,另一种解决方案可能是:
lo8azlld6#
我设计了这个。假设shell库文件是以下文件,名为aLib.sh:
在开始时,它检查并警告检测到任何函数名称冲突。最后,bash已经获得了所有函数的来源,因此它释放了它们的内存,只保留选中的函数。
可以这样使用:
~