2012-03-02 3 views
8

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

+0

Вы используете lua 5.2 или 5.1? – kikito

+1

Нашел: 'х = LoadFile ("myfile.lua")' ' setfenv (х, ENV)' ' х() - все глобальные доступы пойдет ENV, а затем _G' – Milind

+0

Точно. Кажется, вы в 5.1. Вы должны ответить самим себе и пометить свой ответ как правильный, поэтому он не отображается «без ответа», но у вас нет репутации для этого. :/ – kikito

ответ

10

В 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 более подробно освещены в Wiki пользователя Lua. Тема глубже, чем кажется на первый взгляд, но пока вашим пользователям доверяют не злые, тогда практические решения просты.

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

+2

Одним из способов работы с модификацией '_G' является установка' _G' в нечто другое в среде: 'local env = setmetatable ({_ G = false}, {__index = _G})' –

+0

Фактически, я понял, что в скрипте действительно легко сломаться: '_G = nil; _G.print = nil'.Для этого понадобится следующее: 'local env = setmetatable ({}, {__index = function (t, k), если k == '_ G', а затем возвращает nil else return _G [k] end})' –

+0

Одна из ключевых идей выраженный в вики-странице, который я не затронул в своем ответе, заключается в том, чтобы использовать белый список функций, которые тщательно выбираются как безопасные и необходимые * скорее *, чем позволяющие _G или любой другой глобально известной таблице модулей протекать в среде сценария. Это гораздо безопаснее, например, включить 'string.find' в среду, чем' string'. Тем не менее, ответ, который я дал, весьма практичен для конфигурационных файлов небольших незащищенных утилит. – RBerteig

2

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