【问题标题】:Loadfile without polluting global environment在不污染全局环境的情况下加载文件
【发布时间】:2012-03-02 21:39:08
【问题描述】:

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

【问题讨论】:

  • 你使用的是 lua 5.2 还是 5.1?
  • 找到它:x = loadfile("myfile.lua")setfenv(x,env)x() -- all global accesses would go to env rather then _G
  • 没错。您似乎在 5.1 中。您应该自己回答并将您的答案标记为正确,因此它不会显示为“未回答”,但您没有代表。 ://

标签: lua lua-loadfile


【解决方案1】:

在 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 作为索引,不如构造一个只包含已知安全且脚本作者需要的函数的表。

一个完整的解决方案是在沙箱中运行脚本,并可能进一步保护以防止意外(或故意)拒绝服务或更糟糕的情况。 Sandboxes 在 Lua 用户的 Wiki 中有更详细的介绍。这个话题比初看起来更深,但只要您的用户被认为是非恶意的,那么实用的解决方案就很简单。

Lua 5.2 通过删除setfenv() 来支持load() 的新参数来稍微改变一下。详细信息也在 wiki 页面中。

【讨论】:

  • 解决修改_G 的一种方法是将_G 设置为环境中的其他内容:local env = setmetatable({_G=false}, {__index=_G})
  • 其实我意识到在脚本中破解真的很容易:_G=nil; _G.print=nil。所以需要这样的东西:local env = setmetatable({}, {__index=function(t,k) if k=='_G' then return nil else return _G[k] end})
  • 在我的回答中没有提到的 wiki 页面中表达的一个关键想法是使用经过精心挑选的安全和需要的功能白名单而不是允许 _G 或任何其他全球已知的模块表泄漏到脚本的环境中。例如,让string.find 进入环境比让string 本身安全得多。也就是说,我给出的答案对于小型非安全实用程序的配置文件非常实用。
  • 第 2 行中缺少一个右括号?
  • @RobertLamb,在这里我发誓我从工作代码中复制并粘贴了......已修复,谢谢。
【解决方案2】:

这是 RBerteig's 答案的 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,您的回答很有帮助,很有启发性!

【讨论】:

    【解决方案3】:

    在 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
    

    【讨论】:

    • 如果您可以在代码中添加一些解释,答案会更有用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-03-01
    • 1970-01-01
    • 2023-02-10
    • 2023-02-01
    • 1970-01-01
    • 2012-01-30
    • 1970-01-01
    相关资源
    最近更新 更多