Загружать файл без загрязнения глобальной среды

Как я могу загрузить файл таблиц и переменных lua без загрязнения глобальной среды? Поскольку выполнение загрузочного файла и его запуск просто загружают все в глобальное пространство и могут перезаписывать что-то еще, чего я не хочу.

2 ответа

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

Обратите внимание, что сделать глобальные видимости видимыми для script является удобством. Хотя глобальные переменные не могут быть изменены с помощью script очевидным образом, _G - это глобальная переменная, содержащая ссылку на глобальную среду (содержащую _G._G, _G._G._G и т.д.) И _G может быть изменен из script, что может привести к дальнейшим проблемам.

Поэтому вместо того, чтобы использовать _G для индекса, было бы гораздо лучше построить таблицу, содержащую только те функции, которые, как известно, являются безопасными и, как известно, необходимы вашему script автору.

Полное решение состояло бы в том, чтобы запустить script в песочнице и, возможно, дополнительно защищен для предотвращения случайного (или преднамеренного) отказа в обслуживании или еще хуже. Sandboxes более подробно освещены в Lua User Wiki. Тема глубже, чем кажется на первый взгляд, но пока вашим пользователям доверяют не злые, тогда практические решения просты.

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


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

licensed under cc by-sa 3.0 with attribution.