2012-03-02 6 views
8

Come posso caricare un file di tavoli LUA e le variabili senza inquinare l'ambiente globale? Dal momento che eseguire un file di caricamento e eseguirlo carica tutto nello spazio globale e potrebbe sovrascrivere qualcos'altro che non desidero.LoadFile senza inquinare l'ambiente globale

+0

Si sta utilizzando lua 5.2 o 5.1? – kikito

+1

trovato: 'x = loadfile ("myfile.lua")' ' setfenv (x, ENV)' ' x() - tutti gli accessi a livello mondiale sarebbe andato a env piuttosto che _G' – Milind

+0

Esattamente. Sembra che tu sia in 5.1. Dovresti risponderti e contrassegnare la tua risposta come corretta, in modo che non appaia "senza risposta", ma per questo non hai il rappresentante. :/ – kikito

risposta

10

In Lua 5.1 e senza molto la gestione degli errori si potrebbe fare questo:

-- 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 

La prima riga crea una tabella ambiente vuoto che può vedere tutte le variabili globali esistenti, ma che non può banalmente cambiarli dal momento che sono visibili solo per procura attraverso il metametodo __index. Eventuali globals creati dallo script verranno archiviati in env, che viene restituito. Ciò funzionerà bene per gli script semplici che impostano solo una serie di parametri di configurazione e che potrebbero dover chiamare semplici funzioni sicure per impostarli in base alle condizioni in fase di esecuzione.

Nota che, per rendere le variabili globali visibili allo script è una convenienza. Sebbene le variabili globali non possono essere modificati dallo script in modo ovvio, _G è una variabile globale che contiene un riferimento per l'ambiente globale (contenente _G._G, _G._G._G, ecc ...) e _G può essere modificato dallo script che potrebbe portare a ulteriori problemi.

Quindi piuttosto che utilizzare _G per l'indice, sarebbe molto meglio costruire una tabella che contenga solo le funzioni note per essere sicure e note per essere necessarie all'autore del proprio script.

Una soluzione completa potrebbe essere quella di eseguire lo script in una sandbox, ed eventualmente con un'ulteriore protezione per evitare accidentale (o intenzionale) negazione del servizio o peggio. Sandboxes sono trattati in modo più dettagliato nella Wiki dell'utente Lua. L'argomento è più profondo di quanto sembri a prima vista, ma finché gli utenti sono considerati non dannosi, le soluzioni pratiche sono semplici.

Lua 5.2 modifica leggermente le cose eliminando setfenv() in favore di un nuovo parametro su load(). I dettagli sono anche nella pagina wiki.

+2

Un modo per aggirare la modifica '_G' è quello di impostare' _G' a qualcos'altro per l'ambiente: 'ENV locale = SetMetaTable ({_ G = false}, {__index = _G})' –

+0

In realtà, ho capito che è davvero facile spezzare lo script: '_G = nil; _G.print = quelle negative.Quindi qualcosa del genere sarebbe necessario: 'env locale = setmetatable ({}, {__index = funzione (t, k) se k == '_ G' quindi restituisce nil else return _G [k] end})' –

+0

Un'idea chiave espresso nella pagina wiki che non ho sollevato nella mia risposta è di usare una whitelist di funzioni che sono accuratamente selezionate come sicure e necessarie * piuttosto * che permettere a _G o qualsiasi altra tabella di moduli globalmente nota di filtrare nell'ambiente dello script. È molto più sicuro lasciare 'stringa.find' nell'ambiente che' stringa' stessa, per esempio. Detto questo, la risposta che ho dato è abbastanza pratica per i file di configurazione di piccole utilità non sicure. – RBerteig

2

Ecco un dofile() versione di risposta di RBerteig in cui si fornisce l'ambiente e il risultato, se presente, viene restituito (ho provato a fare questo come un commento, ma non riuscivo a capire in formato esso):

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 

ho voluto essere in grado di caricare più file in uno stesso ambiente, e alcuni di questi file ha avuto un 'restituire qualcosa' in loro. Grazie RBerteig, la tua risposta è stata utile e istruttiva!