shell脚本模块化开发核心是用source(或.)在当前shell中执行独立文件,使函数变量立即可用;模块文件需命名规范、加守卫变量、用local声明变量、避免隐式依赖;引入时注意路径可移植性,防止重复加载和作用域污染。

Shell脚本模块化开发的核心就是用 source(或简写为 .)把函数、配置或工具逻辑拆到独立文件里,再按需加载——它不是“导入”,而是“在当前 Shell 中直接执行”,所以定义的函数和变量立刻可用。
怎么写一个可复用的模块文件
模块文件(比如 lib_utils.sh)只做一件事:声明函数,不执行任何命令。常见规范包括:
- 函数名全小写+下划线,如
log_debug、is_dir_empty,避免和系统命令冲突 - 开头加守卫变量,防止重复加载:
[[ ${LIB_UTILS_SH_LOADED} ]] && return; LIB_UTILS_SH_LOADED=1 - 所有变量尽量用
local声明,不污染全局作用域 - 不依赖未显式传入的外部变量;需要配置时,提供初始化函数或通过参数控制
在主脚本中正确引入模块
source 的路径写法直接影响可移植性,推荐按场景选择:
- 同目录:
source ./lib_utils.sh - 子目录:
source ./lib/network.sh - 固定系统路径:
source /usr/local/lib/shell/log.sh - 利用 PATH 搜索(需确保模块在 PATH 目录下且有执行权限):
source "$(command -v mylib.sh)"
注意:source 和 . 完全等价,但点号后必须空一格,比如 . ./lib.sh,不能写成 . ./lib.sh(无空格会报错)。
为什么函数能直接调用?
因为 source 不启动子 Shell,而是把模块里的代码一行行在当前环境中运行。函数定义语句被执行后,就注册进了当前 Shell 的函数表,后续随时可调用,就像自己写的函数一样。例如:
- 模块中定义:
greet() { echo "Hi, $1"; } - 主脚本
source ./lib.sh后,直接写greet Alice就能输出Hi, Alice
常见陷阱与应对
模块化容易出问题的地方往往不在语法,而在加载逻辑和作用域:
- 重复
source同一文件会导致函数重定义(可能报 warning 或覆盖),守卫机制必不可少 - 相对路径依赖当前工作目录,建议用
$(dirname "$0")/lib/xxx.sh获取脚本所在目录再拼接,增强健壮性 - 模块中用了
exit会终止整个主脚本,应改用return或明确错误码返回 - 调试时可加
set -x查看source过程中实际执行了哪些语句











