我正在编写一个允许本地函数命名空间的实验性 Bash 模块系统,我的第一个想法是编写一个 Bash 函数解析器,它将逐行读取函数代码,并在每个函数/变量名称前面加上<module-name>.
(即模块module
中的函数func
将变为module.func
- 可以再次导入到另一个模块中,如module_2.module.func
等等; 函数内的变量将被名称破坏 - 变量 模块module
中的函数func
内的var
将变为__module_func_var
(。
但是,为了做到这一点,我需要一种方法来检测哪些名称是变量,并用传输的导入名称替换它们在函数中的所有出现。像variable=[...]
这样的琐碎情况很容易解析,但还有无数其他情况不是那么微不足道——while read variable; do [...] done
和variable2="asdf${variable//_/+}"
呢?
在我看来,为了做到这一点,我需要深入研究 Bash 的解析机制或阅读一本关于编程语言的书 - 但是为了实现我上面解释的内容,我从哪里开始呢?
我需要一种方法来检测哪些名称是变量
我很抱歉这么说,但总的来说这是不可能的。
仅支持可能发生变量的静态情况是可能的,但非常棘手。只考虑变量赋值:除了x=
之外,还有declare x=
、printf -v x
、read x
、mapfile x
、readarray x
等等。即使是像shellcheck
这样的成熟工具,在正确解析所有这些情况时仍然存在问题(例如,请参阅此问题(。
但是,即使您掌握了正确解析所有静态情况,仍然可以通过动态变量,例如:
x=$(someCommand)
declare "$x=something"
在此示例中,如果不执行someCommand
,您将无法知道新变量的名称。其他同样(甚至(更糟糕的事情是bash
的间接算子${!x}
,算术上下文中的隐式间接(例如x=y; echo $((x))
(,eval
.
tl;dr:获取脚本中所有变量的唯一方法是解释/执行脚本。
但这里又出现了另一个问题:如果存在非确定性(declare "$(tr -cd a-z /dev/urandom | head -c1)=..."
(,执行脚本也不是一种选择。请注意,用户输入也是不确定的(read x; declare "var$x=..."
(。您必须编写静态分析器。但由于停止问题,这也不是一个选择。从停止问题中,我们可以推断出(通常(不可能判断给定的 bash 脚本是否具有有限数量的变量。
要实现您的模块系统,您可以使用另一种方法。例如,如果有人想为您的框架实现一个模块,那么他们必须以简单的可解析格式指定该模块中的函数/变量。