如何在不污染全局环境的情况下加载 lua 表和变量文件?由于执行加载文件并运行它只会加载全局空间中的所有内容,并且可能会覆盖我不想要的其他内容。
3 回答
在 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 用户的 Wiki 中有更详细的介绍。这个话题比乍一看更深,但只要你的用户被认为是非恶意的,那么实际的解决方案就很简单了。
Lua 5.2 通过取消setfenv()
对load()
. 详细信息也在 wiki 页面中。
Here is a dofile() version of RBerteig's answer where you supply the environment and the result, if any, is returned (I tried to do this as a comment, but couldn't figure out to format it):
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
I wanted to be able to load multiple files into the same environment, and some of these files had a 'return something' in them. Thanks RBerteig, your answer was helpful and instructive!
在 Lua > 5.2
function run_test_script(scriptfile)
local env = setmetatable({}, {__index=_G})
assert(pcall(loadfile(scriptfile,"run_test_script",env)))
setmetatable(env, nil)
return env
end