2012-03-02 35 views
8

Làm cách nào để tải một tệp bảng và biến mà không gây ô nhiễm môi trường toàn cầu? Kể từ khi làm một loadfile và chạy nó chỉ cần tải tất cả mọi thứ trong không gian toàn cầu và có thể ghi đè lên một cái gì đó khác mà tôi không muốn.Loadfile mà không gây ô nhiễm môi trường toàn cầu

+0

Bạn đang sử dụng lua 5.2 hoặc 5.1? – kikito

+1

Tìm thấy nó: 'x = loadfile ("myfile.lua") ' ' setfenv (x, env)' 'x() - tất cả các truy cập toàn cầu sẽ đi đến ENV thay vì sau đó _G' – Milind

+0

Chính xác. Dường như bạn đang ở trong 5.1. Bạn nên tự trả lời và đánh dấu câu trả lời là chính xác, vì vậy nó không xuất hiện "chưa được trả lời", nhưng bạn không có đại diện cho điều đó. :/ – kikito

Trả lời

10

Trong Lua 5.1 và không có nhiều lỗi xử lý bạn có thể làm điều này:

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

Dòng đầu tiên tạo ra một bảng môi trường có sản phẩm nào có thể xem tất cả globals hiện có, nhưng mà không thể trivially thay đổi chúng vì chúng là chỉ hiển thị bằng proxy thông qua phương thức metamethod __index. Bất kỳ hình cầu nào mà tập lệnh tạo sẽ được lưu trữ trong env, được trả lại. Điều này sẽ làm việc tốt cho các script đơn giản chỉ cần thiết lập một loạt các tham số cấu hình và có thể cần gọi các hàm an toàn đơn giản để đặt chúng dựa trên các điều kiện trong thời gian chạy.

Lưu ý rằng việc hiển thị hình ảnh chung cho tập lệnh là một sự tiện lợi. Mặc dù các globals không thể sửa đổi từ script theo cách rõ ràng, _G là một biến toàn cầu chứa tham chiếu đến môi trường toàn cục (có chứa _G._G, _G._G._G, v.v.) và _G có thể được sửa đổi từ tập lệnh. các vấn đề khác. Vì vậy, thay vì sử dụng _G cho chỉ mục, sẽ tốt hơn nếu bạn xây dựng một bảng chỉ chứa các hàm được biết là an toàn và được tác giả kịch bản của bạn cần.

Giải pháp hoàn chỉnh sẽ là chạy tập lệnh trong hộp cát và có thể được bảo vệ hơn nữa để ngăn chặn việc từ chối dịch vụ vô tình (hoặc cố tình) hoặc tệ hơn. Sandboxes được trình bày chi tiết hơn tại Wiki của người dùng Lua. Chủ đề sâu hơn so với cái nhìn đầu tiên, nhưng miễn là người dùng của bạn được tin cậy là không độc hại thì các giải pháp thực tế rất đơn giản.

Lua 5.2 thay đổi mọi thứ một chút bằng cách xóa setfenv() có lợi cho thông số mới thành load(). Chi tiết cũng nằm trong trang wiki.

+2

Một cách để sửa đổi '_G' là đặt' _G' thành một cái gì đó khác trong môi trường: 'local env = setmetatable ({_ G = false}, {__index = _G})' –

+0

Thực ra, tôi nhận ra rằng nó thực sự dễ dàng để phá vỡ trong kịch bản: '_G = nil; _G.print = nil'.Vì vậy, một cái gì đó như thế này sẽ là cần thiết: 'local env = setmetatable ({}, {__index = function (t, k) nếu k == '_ G' sau đó trả về nil khác return _G [k] end})' –

+0

Một ý tưởng chính thể hiện trong trang wiki mà tôi không đưa ra trong câu trả lời là sử dụng danh sách trắng các hàm được chọn cẩn thận là an toàn và cần thiết * thay vì * cho phép _G hoặc bất kỳ bảng mô-đun được biết đến khác trên toàn cầu rò rỉ vào môi trường tập lệnh. Ví dụ, để cho 'string.find' trở thành môi trường an toàn hơn là' string'. Điều đó nói rằng, câu trả lời như tôi đã cho nó là khá thiết thực để cấu hình các tập tin của các tiện ích không an toàn nhỏ. – RBerteig

2

Đây là một dofile() phiên bản của câu trả lời RBerteig của nơi bạn cung cấp môi trường và kết quả, nếu có, sẽ được trả về (tôi đã cố gắng để làm điều này như một bình luận, nhưng không thể tìm ra sang định dạng nó):

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 

Tôi muốn có thể tải nhiều tệp vào cùng một môi trường và một số tệp trong số đó có 'trả lại nội dung' trong đó. Cảm ơn RBerteig, câu trả lời của bạn rất hữu ích và có tính hướng dẫn!

Các vấn đề liên quan