9

如何在不污染全局环境的情况下加载 lua 表和变量文件?由于执行加载文件并运行它只会加载全局空间中的所有内容,并且可能会覆盖我不想要的其他内容。

4

3 回答 3

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 用户的 Wiki 中有更详细的介绍。这个话题比乍一看更深,但只要你的用户被认为是非恶意的,那么实际的解决方案就很简单了。

Lua 5.2 通过取消setfenv()load(). 详细信息也在 wiki 页面中。

于 2012-03-02T22:39:58.117 回答
3

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!

于 2013-05-03T20:06:58.533 回答
0

在 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
于 2021-11-10T09:15:30.070 回答