现在问题出在
.
(或Bash中的
source
)。它不仅解析函数定义,还执行脚本中的代码。
以下是我喜欢的一种方法,可以确保只有在执行文件时才运行我的Bash代码。
我喜欢让它看起来更像Python,而不是其他答案。我做了一些额外的工作,以便最终得到
if [ "$__name__" = "__main__" ]; then
这一行。请参见我在此处的答案:
What is the bash equivalent to Python's if __name__ == '__main__'
?,其中我描述了这个过程。
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
__name__="__main__"
else
__name__="__source__"
fi
if [ "$__name__" = "__main__" ]; then
main "$@"
fi
让我们更深入地了解一下,我会提供一个完整的库示例和各种导入方式:
详细示例:如何在Bash中编写、导入、使用和测试库?
这里是一种非常漂亮、几乎类似Python风格的方法,我在多年的编写和使用Bash库中总结出来的。Bash是一种美丽的“粘合”类型语言,可以轻松地将来自许多语言的可执行文件组合在一起。考虑到Bash已经存在很长时间了,我不确定为什么以下样式不太受欢迎,但也许之前没有人想过或以这种方式使用。所以,来吧,我认为你一定会发现它非常有用。
您还可以在我的eRCaGuy_hello_world存储库中的hello_world_best.sh文件中看到我用于所有bash脚本的一般起点。
您可以在浮点数数学.sh中查看完整的库示例。
library_basic_example.sh:
#!/usr/bin/env bash
RETURN_CODE_SUCCESS=0
RETURN_CODE_ERROR=1
my_func1() {
echo "100.1"
}
my_func2() {
echo "200"
}
my_func3() {
echo "hello world"
}
_assert_eq() {
if [ "$1" != "$2" ]; then
echo "Error: assertion failed. Arguments not equal!"
echo " arg1 = $1; arg2 = $2"
echo "Exiting."
exit $RETURN_CODE_ERROR
fi
}
_test() {
printf "%s\n\n" "Running tests."
printf "%s\n" "Running 'my_func1'"
result="$(my_func1)"
printf "%s\n\n" "result = $result"
_assert_eq "$result" "100.1"
printf "%s\n" "Running 'my_func2'"
result="$(my_func2)"
printf "%s\n\n" "result = $result"
_assert_eq "$result" "200"
printf "%s\n" "Running 'my_func3'"
result="$(my_func3)"
printf "%s\n\n" "result = $result"
_assert_eq "$result" "hello world"
echo "All tests passed!"
}
main() {
_test
}
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
__name__="__main__"
else
__name__="__source__"
fi
if [ "$__name__" = "__main__" ]; then
main "$@"
fi
运行库以运行其单元测试
现在,使文件可执行。运行它将运行其内部单元测试:
chmod +x library_basic_example.sh
./library_basic_example.sh
示例运行命令和输出:
eRCaGuy_hello_world$ bash/library_basic_example.sh
Running tests.
Running 'my_func1'
result = 100.1
Running 'my_func2'
result = 200
Running 'my_func3'
result = hello world
All tests passed!
导入(源)库
要导入Bash库,您可以使用source
或.
(更好)命令进行“源”操作。在这里阅读有关此问题的更多信息:{{link1:source
( .
)与export
(以及末尾的一些文件锁[flock
]内容)}}。
1. 使用手动设置的导入路径
您可以直接在bash终端中执行此操作,也可以在自己的bash脚本中执行此操作。现在就在您自己的终端中尝试吧!:
source "path/to/library_basic_example.sh"
. "path/to/library_basic_example.sh"
一旦你导入了Bash库(import),你就可以直接调用其中的函数。下面是一个完整的示例运行和输出,展示了我可以像my_func1
、my_func2
等一样,在终端内直接调用这个Bash库一旦我已经导入了它!:
eRCaGuy_hello_world$ . bash/library_basic_example.sh
eRCaGuy_hello_world$ my_func1
100.1
eRCaGuy_hello_world$ my_func2
200
eRCaGuy_hello_world$ my_func3
hello world
eRCaGuy_hello_world$ _test
Running tests.
Running 'my_func1'
result = 100.1
Running 'my_func2'
result = 200
Running 'my_func3'
result = hello world
All tests passed!
2. 使用 BASHLIBS
环境变量,使导入您的 Bash 库更加容易
您可以从 任何路径 导入您的 bash 库。但是,像 BASHLIBS
这样的环境变量会使它更容易。将其添加到您的 ~/.bashrc
文件底部:
if [ -d "$HOME/libs_bash/libraries" ] ; then
export BASHLIBS="$HOME/libs_bash/libraries"
fi
现在,您可以将Bash库的符号链接创建到该目录中,就像这样:
mkdir -p ~/libs_bash/libraries
cd path/to/dir_where_my_library_file_of_interest_is_stored
ln -si "$(pwd)/library_basic_example.sh" ~/libs_bash/libraries/
现在,我的library_basic_example.sh
文件的符号链接存储在~/libs_bash/libraries/
中,并且已经设置了BASHLIBS
环境变量并导出到我的环境中。因此,我可以将我的库导入到任何Bash终端或在终端内运行的脚本中,就像这样:
. "$BASHLIBS/library_basic_example.sh"
3. [我最常用的技巧!] 使用相对导入路径
这个技巧非常漂亮和强大。看看这个例子吧!
假设您有以下目录布局:
dir1/
my_lib_1.sh
dir2/
my_lib_2.sh
my_script.sh
dir3/
my_lib_3.sh
以上的表示方式在大型程序或工具链中很容易存在,其中您设置了散乱的可运行脚本和库文件,特别是在您的各种bash脚本之间共享(源/导入)代码时,无论它们在何处。
假设您有以下约束/要求:
- 上述整个目录结构都存储在单个GitHub仓库中。
- 您需要任何人都能够
git clone
此repo并让所有脚本和导入等内容为每个人“神奇地工作”!
- 这意味着您需要在将bash脚本相互导入时使用“相对导入”。
- 例如,您将运行
my_script.sh
。
- 您必须能够从任何地方调用
my_script.sh
,这意味着:您应该能够在调用此脚本运行时进入您整个文件系统中的任何目录。
my_script.sh
必须使用相对导入来导入my_lib_1.sh
,my_lib_2.sh
和my_lib_3.sh
。
这就是方法!基本上,我们要找到运行脚本的路径,然后将该路径用作相对起点,以处理该脚本周围的其他脚本!
阅读我的完整答案以获取更多细节:如何获取正在运行或引用的任何脚本的完整文件路径、完整目录和基本文件名...即使被调用的脚本是从另一个bash函数或脚本中调用的,或者在使用嵌套引用时!。
完整示例:
my_script.sh:
#!/usr/bin/env bash
FULL_PATH_TO_SCRIPT="$(realpath "${BASH_SOURCE[-1]}")"
SCRIPT_DIRECTORY="$(dirname "$FULL_PATH_TO_SCRIPT")"
. "$SCRIPT_DIRECTORY/../my_lib_1.sh"
. "$SCRIPT_DIRECTORY/my_lib_2.sh"
. "$SCRIPT_DIRECTORY/dir3/my_lib_3.sh"
就是这样!如果你知道命令,那么非常容易!
Python 能做到这一点吗?至少原生的 Python 不能。在这方面,Bash 比 Python 更加简单!当 import
时,Python 不会直接使用文件系统路径。它比那复杂和曲折得多。但是,我正在开发一个名为 import_helper.py
的 Python 模块,以使 Python 中的此类操作变得容易。我很快也会发布它。
更进一步
- 在我的eRCaGuy_hello_world存储库中查看我的完整且非常有用的Bash浮点库,链接在这里:floating_point_math.sh。
- 在我的自述文件中查看我关于Bash库安装和使用的一些备选笔记,链接在这里:https://github.com/ElectricRCAircraftGuy/eRCaGuy_hello_world/tree/master/bash/libraries
另请参阅
- 测试Bash shell脚本 - 这个答案提到了这个
assert.sh
Bash仓库, 看起来非常有用,可以进行更加健壮的Bash单元测试!
- Bash和测试驱动开发
- 对Bash脚本进行单元测试
注意:我将这个答案从这里迁移过来,那里我不小心创建了一个重复的问答。
if __name__ == '__main__'
的语句?。我在那里添加了一个非常类似Python的答案,供有兴趣的人参考。 - Gabriel Staples