如何在不污染全局环境的情况下加载lua表和变量的文件?因为做一个加载文件并运行它只是加载全局空间中的所有内容,可能会覆盖我不想要的其他内容。
在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页面中。
这是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,你的回答很有帮助,很有启发性!
在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