Loadfile без загрязнения окружающей среды

Как я могу загрузить файл таблиц и переменных 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 User's Wiki. Эта тема глубже, чем кажется на первый взгляд, но если ваши пользователи уверены, что они не являются злонамеренными, то практические решения просты.

Lua 5.2 немного меняет вещи, устраняя setfenv() в пользу нового параметра load(), Подробности также в вики-странице.

Вот dofile() версия ответа RBerteig, в которой вы предоставляете среду, и результат, если таковой имеется, возвращается (я пытался сделать это как комментарий, но не смог придумать, как его отформатировать):

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, ваш ответ был полезным и поучительным!

В Луа > 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
Другие вопросы по тегам