如何在不污染全局环境的情况下加载lua表和变量文件?由于执行加载文件并运行它只是加载全局空间中的所有内容,并且可能会覆盖我不想要的其他内容。
答案 0 :(得分:12)
在Lua 5.1中,如果没有太多的错误处理,你可以这样做:
-- load and run a script in the provided environment
-- returns the modified environment table
function run(scriptfile)
local env = setmetatable({}, {__index=_G})
assert(pcall(setfenv(assert(loadfile(scriptfile)), env)))
setmetatable(env, nil)
return env
end
第一行创建一个空的环境表,可以看到所有现有的全局变量,但是它们不能轻易地更改它们,因为它们只能通过代理通过__index
元方法看到。脚本创建的任何全局变量都将存储在env
中,并返回该变量。这对于只设置一堆配置参数的简单脚本很有效,而且可能需要调用简单的安全函数来根据运行时的条件设置它们。
请注意,使全局变量对脚本可见是一种方便。尽管无法以明显的方式从脚本中修改全局变量,但_G
是一个全局变量,其中包含对全局环境的引用(包含_G._G
,_G._G._G
等...)并且可以从脚本中修改_G
,这可能会导致进一步的问题。
因此,不是使用_G
作为索引,而是构建一个只包含已知安全且已知脚本作者需要的函数的表格会更好。
完整的解决方案是在沙箱中运行脚本,并可能进一步保护以防止意外(或故意)拒绝服务或更糟。 Lua User's Wiki上更详细地介绍了Sandboxes。这个主题比初看起来更深入,但只要您的用户被信任为非恶意用户,那么实用的解决方案就很简单。
Lua 5.2通过取消setfenv()
来支持load()
的新参数,从而稍微改变了一些事情。详细信息也在维基页面中。
答案 1 :(得分:3)
这是 RBerteig 答案的 dofile()版本,您提供环境并返回结果(如果有的话)(我尝试将此作为评论) ,但无法弄清楚它的格式):
local function DofileIntoEnv(filename, env)
setmetatable ( env, { __index = _G } )
local status, result = assert(pcall(setfenv(assert(loadfile(filename)), env)))
setmetatable(env, nil)
return result
end
我希望能够将多个文件加载到同一个环境中,其中一些文件中有一些“返回内容”。感谢RBerteig,您的回答很有帮助,也很有启发性!