擴充套件建議案 |
|
建議案是一個 API 和實作,用於 os 和 io 名稱空間中的額外 non-ANSI 函數。
最近(2006 年 1 月)在郵件清單中的討論促使我嘗試設計一個延伸 API,它透過將函數新增到 os 和 io 名稱空間來延伸 Lua API。
這並非修改 Lua 核心程式的建議案,而是一個延伸 Lua 核心的 API 設計建議案。此 API 旨在為現代流行作業系統(Windows、MacOSX 和 POSIX 平台)上的獨立 Lua 程式提供更完整的程式設計環境。
LuaForge 上有存放 [適用於 POSIX 和 Windows 的實作]。這些都是非常實用的實作,但在 API 仍在標準化的過程中,只應視為測試目的而使用。
請注意,所有這些函數在失敗時都會傳回標準的 (nil、「錯誤訊息」),並且在未另行指定的情況下,它們在成功時會傳回 (true)。
require "ex"
os.getenv(name)
os.setenv(name, value)
os.setenv(name, nil)
os.environ()
os.chdir(pathname)
os.mkdir(pathname)
os.remove(pathname)
pathname = os.currentdir()
for entry in os.dir(pathname) do ; end
entry = os.dirent(pathname) entry = os.dirent(file)
os.dir() 傳回的迭代器函數和 os.dirent() 函數都傳回一個「項目」表格。此表格至少包含以下欄位
實作可能會新增其他欄位,甚至方法。
file:lock(mode, offset, length) io.lock(file, mode, offset, length)
file:unlock(offset, length) io.unlock(file, offset, length)
請注意,file:lock() 和 file:unlock() 都會延伸 Lua 檔案物件的元表格。
rd, wr = io.pipe()
os.sleep(seconds) os.sleep(interval, unit)
os.sleep(3.8) -- sleep for 3.8 seconds local microseconds = 1e6 os.sleep(3800000, microseconds) -- sleep for 3800000 �s local ticks = 100 os.sleep(380, ticks) -- sleep for 380 ticks
proc = os.spawn(filename) proc = os.spawn{filename, [args-opts]} proc = os.spawn{command=filename, [args-opts]}
exitcode = proc:wait()
所有函數也都在 ex 名稱空間中
ex.getenv(name) ex.setenv(name, value) ex.environ() ex.chdir(pathname) ex.mkdir(pathname) ex.currentdir() ex.dir(pathname) ex.dirent(pathname) ex.lock(file, mode, offset, length) ex.unlock(file, offset, length) ex.pipe() ex.sleep(interval, [unit]) ex.spawn(...) ex.wait(proc)
請注意,ex.getenv 在這裡主要是為了並行性,另外也是因為在 Windows 中,如果要使用 SetEnvironmentVariable?() API,則必須覆寫標準的 os.getenv,其會使用 getenv() 透過 GetEnvironmentVariable?() 來使用。
require "ex" -- run the echo command proc = os.spawn"/bin/echo" proc = os.spawn{"/bin/echo", "hello", "world"} proc = os.spawn{command="/bin/echo", "hello", "world"} -- run the id command vars = { LANG="fr_FR" } proc = os.spawn{"/bin/id", "-un", env=vars} proc = os.spawn{command="/bin/id", "-un", env=vars) -- Useless use of cat local rd, wr = assert(io.pipe()) local proc = assert(os.spawn("/bin/cat", {stdin=rd})) rd:close() wr:write("Hello world\n") wr:close() proc:wait() -- Run a program with a modified environment local env = os.environ() env.LUA_PATH = "/usr/share/lib/lua/?.lua" env.LUA_CPATH = "/usr/share/lib/lua/?.so" local proc = assert(os.spawn("lua", {args = {"-e", 'print"Hello world\n"'}, env=env })) proc:wait() -- popen2() function popen2(...) local in_rd, in_wr = io.pipe() local out_rd, out_wr = io.pipe() local proc, err = os.spawn{stdin = in_rd, stdout = out_wr, ...} in_rd:close(); out_wr:close() if not proc then in_wr:close(); out_rd:close() return proc, err end return proc, out_rd, in_wr end -- usage: local p, i, o = assert(popen2("wc", "-w")) o:write("Hello world"); o:close() print(i:read"*l"); i:close() p:wait()
Mark,與其將函數新增至標準函式庫名稱空間,我建議您將您的作品視為一個工具模組,為模組取一個名稱,並將函數置於新的模組名稱之下。如果您的函數像現在這樣流傳開來,將會造成混淆,特別是對於 Lua 初學者來說,因為他們可能會讀到範例程式碼,卻不明白為何標準模組中某些函數不在上游文件之中。--JohnBelmonte
我同意 John 的看法。或許可使用 ex.install() 或類似的模組將它安裝至 os 函式庫。但還是希望它是個別的函式庫。--Doug Rogers
我同意之前的評論。我不希望將函數注入「os」,而是希望有獨立的函式庫。'
env.getenv(name) env.setenv(name, value) env.environ() --no unsetenv. It's meaningless. --function env.unsetenv(name) -- env.setenv(name, nil) --end fs.chdir(pathname) fs.mkdir(pathname) fs.currentdir() fs.dir(pathname) fs.dirent(pathname) fs.lock(file, mode, offset, length) fs.unlock(file, offset, length) proc.pipe() proc.sleep(seconds) proc.spawn(...) proc.wait(proc)
Mark,感謝您的實現。在此同意避免「污染」os 和 io 模組,至少在目前的實現中如此,如此一來,使用者就能在不會有衝突危險的狀況下於現有製作碼中使用這些模組(如果和何時擴充 os 和 io 名稱空間的介面被標準化)。正如所述,您的實現(而非建議的介面)可能有一個 ex.install()
或類似的函數,可根據介面建議更新 os
和 io
。這讓使用者可以暫時做出選擇。--DavidManura
進一步來說,如果要將其記錄到它自己的資料表(例如文件系統)中,這麼做可能是個好方法:從作業系統中複製有關檔案的條目。這有助於讓新程式碼更乾淨,並在呼叫這個延伸時「過時」作業系統資料表。
(1) 在預設的 conf.in 中,將「-llua51」改為「-llua5.1」,這樣做和 LuaBinaries 比較相符嗎?
這和 Lua 發行版本相符,不過我可以在 conf.in 中新增註解。-Mark
我一直感到納悶,Lua 和 LuaBinaries 之間為什麼有這種不一致性。
(2) os.sleep(math.huge) 立即傳回。它不應該傳回嗎?
我需要找出為什麼會這樣…-Mark
os.sleep(1e100) 也立即傳回。儘管 Win32 Sleep(INFINITE) 或任何龐大值似乎不太有用,不過誰知道呢(INFINTE 在 SleepEx 中似乎是有用的 ?)。 [1] Sleep(0) 的意思是「放棄時間片」。
(3) os.setenv 和 os.mkdir(1)-實作會自動將數值參數轉換成字串。這是故意的嗎?
很可能是的,它們預期得到的是字串。-Mark
我在 字串.lower(123) == "123"
(為真)中也看到相同的行為。
(4) 這是故意的嗎?
> =os.setenv("a", {}) nil 203 (0xCB): The system could not find the environment option that was entered. > =os.setenv("a", nil) nil 203 (0xCB): The system could not find the environment option that was entered.
API 沒有說明如果將非字串傳遞為第二項論證,或者如果嘗試移除不存在的變數時,會發生什麼事。它應該這樣做嗎?
不知道,不過錯誤訊息很奇怪。
(5) 要讓 os.dir() 在沒有參數的情況下,使用目前的工作目錄嗎?
這是一個好點子;那時它需要成為 API 的一部份。-Mark
我呼叫 os.diren 時,entry.name 不見了。
是的,這是故意的,而且 API 的說明不正確。除非有不同的理由嗎?-Mark
(7) 這是故意的嗎?
> f = io.open("123") > = io.lock(f, "w") nil 6 (0x6): The handle is invalid. > =f file (0080F060)
是的,ex 沒有說明如果嘗試對唯讀檔案寫入鎖定時,會發生什麼事。它應該說明嗎?-Mark
也許不用。這在 Win32 中又是怎麼一回事?
Lua 5.1.1 Copyright (C) 1994-2006 Lua.org, PUC-Rio > require "ex" > f = assert(io.open("234", "w")) > = f:lock("w") nil 6 (0x6): The handle is invalid.
這是個 bug;我想現在已經修正了。-Mark
(8)
$ make mingw make -C w32api ex.dll make[1]: Entering directory `.../ex/w32api' cc -W -Wall -Wno-missing-braces -DWIN32_LEAN_AND_MEAN -DNOGDI -... -mno-cygwin -c -o ex.o ex.c cc -W -Wall -Wno-missing-braces -DWIN32_LEAN_AND_MEAN -DNOGDI -... -mno-cygwin -c -o spawn.o spawn.c spawn.c: In function `spawn_param_init': spawn.c:35: warning: missing initializer spawn.c:35: warning: (near initialization for `si.lpReserved') cc -W -Wall -Wno-missing-braces -DWIN32_LEAN_AND_MEAN -DNOGDI -I... -mno-cygwin -c -o pusherror.o pusherror.c cc -W -Wall -Wno-missing-braces -DWIN32_LEAN_AND_MEAN -DNOGDI -I... -mno-cygwin -c -o dirent.o dirent.c cc -mno-cygwin -shared -o ex.dll ex.o spawn.o pusherror.o dirent.o -L... -llua5.1 make[1]: Leaving directory `.../ex/w32api'
參閱 http://msdn2.microsoft.com/en-us/library/ms686331.aspx 可以使用 memset 初始化結構,以避免警告。
這個警告是 GCC 不幸的一個特點,C 保證其他的結構成員預設初始化為零,這是此處的目的。-Mark
可以透過以下方式避免這個警告:「static const STARTUPINFO si = {sizeof si, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};」
(9)
$ make cygwin make -C posix T=ex.dll ex.dll make[1]: Entering directory `...ex/posix' cc -W -Wall -std=c89 -D_XOPEN_SOURCE=600 -DMISSING_POSIX_SPAWN -DENVIRON_DECL="e xtern char **environ;" -I... -c -o ex.o ex.c ex.c: In function `ex_setenv': ex.c:53: warning: implicit declaration of function `setenv' ex.c:53: warning: implicit declaration of function `unsetenv' ex.c: In function `new_file': ex.c:131: warning: implicit declaration of function `fdopen' ex.c:131: warning: assignment makes pointer from integer without a cast ex.c: In function `ex_dirent': ex.c:151: warning: implicit declaration of function `fileno' cc -W -Wall -std=c89 -D_XOPEN_SOURCE=600 -DMISSING_POSIX_SPAWN -DENVIRON_DECL="e xtern char **environ;" -I... -c -o spawn.o spawn.c cc -W -Wall -std=c89 -D_XOPEN_SOURCE=600 -DMISSING_POSIX_SPAWN -DENVIRON_DECL="e xtern char **environ;" -I... -c -o posix_spawn.o po six_spawn.c posix_spawn.c:49: warning: unused parameter 'act' cc -shared -o ex.dll ex.o spawn.o posix_spawn.o -L... make[1]: Leaving directory `...ex/posix'
請注意 cygwin 標準庫 h
#ifndef __STRICT_ANSI__ long _EXFUN(a64l,(const char *__input)); char * _EXFUN(l64a,(long __input)); char * _EXFUN(_l64a_r,(struct _reent *,long __input)); int _EXFUN(on_exit,(_VOID (*__func)(int, _PTR),_PTR __arg)); _VOID _EXFUN(_Exit,(int __status) _ATTRIBUTE ((noreturn))); int _EXFUN(putenv,(char *__string)); int _EXFUN(_putenv_r,(struct _reent *, char *__string)); int _EXFUN(setenv,(const char *__string, const char *__value, int __overwrite)); int _EXFUN(_setenv_r,(struct _reent *, const char *__string, const char *__value, int __overwrite)) #ifndef __STRICT_ANSI__ #ifndef _REENT_ONLY FILE * _EXFUN(fdopen, (int, const char *)); #endif int _EXFUN(fileno, (FILE *));
ExtensionProposal 的目的是提供非 ANSI 的函數,為什麼要使用 ANSI 編譯?
沒錯,-std=c89 不正確,我會修改 conf.in。-Mark
-- DavidManura
(10) ex.dir 似乎會開啟檔案,但不會關閉它。因此,這個程式碼
while true do for f in os.dir("./") do print(f.name) end end
會產生以下錯誤
stdin:2:嘗試呼叫 nil 值堆疊追蹤
循環過幾次後,other 呼叫 to os.dir 給出
print(os.dir("./")) -> nil 開啟過多檔案
lsof 顯示有開啟該目錄的多個 (1020) 副本。
我理解開啟的檔案一定是多重呼叫 ex API 的一部分,但使用開啟的檔案呼叫 os.dir 的可能性可以提供暫時解決方法
while true do local d = io.open("./") for f in os.dir(d) do print(f.name) end d:close() end
順帶一提,此範例非常有用。棒透了 -- m.i.
快速搜尋過 POSIX 手冊後,這似乎不容易或完全不可能。相反地,修改 posix/ex.c 中的幾行 (在第 240 行 ex_dir 中) 將
if (!d) return push_error(L);
改為
if (!d) { diriter_close(L); return push_error(L); }
似乎解決了這個問題 (而且未製造出其他問題,但需要測試)。
抱歉沒有使用 diff,我從未學過...
再次感謝你的貢獻 -- m.i.
是的,檔案處理在記憶體耗盡前就已經用罄,所以你的方法是正確的解決方案。順便一提,這也許是個更好的習慣用語
for f in assert(os.dir".") do print f.name; end
-Mark
是的,這是已知的錯誤,我已經找到解決方法,我只要把它張貼出來就好,抱歉。-Mark
很高興知道... 但你所知的這些錯誤是什麼?如果你知道這些錯誤,也許與更多人分享這些知識會是個好主意?比方說,就在這個頁面中?好吧... <搖頭> -- ThomasLauer
void spawn_param_args(struct spawn_params *p) { lua_State *L = p->L; debug("spawn_param_args:"); debug_stack(L); luaL_Buffer args; luaL_buffinit(L, &args); size_t i, n = lua_objlen(L, -1); /* concatenate the arg array to a string */ for (i = 1; i <= n; i++) { int quote; lua_rawgeti(L, -1, i); /* ... argtab arg */ /* XXX checkstring is confusing here */ quote = needs_quoting(luaL_checkstring(L, -1)); luaL_putchar(&args, ' '); if (quote) luaL_putchar(&args, '"'); luaL_addvalue(&args); if (quote) luaL_putchar(&args, '"'); ---> lua_pop(L, 1); /* ... argtab */ } luaL_pushresult(&args); /* ... argtab argstr */ lua_pushvalue(L, 1); /* cmd opts ... argtab argstr cmd */ lua_insert(L, -2); /* cmd opts ... argtab cmd argstr */ lua_concat(L, 2); /* cmd opts ... argtab cmdline */ lua_replace(L, -2); /* cmd opts ... cmdline */ p->cmdline = lua_tostring(L, -1); }
require 'ex' ex.spawn{'ls'}
gdb 執行追蹤
#0 0x00002adc9351a885 in raise () from /lib/libc.so.6 #1 0x00002adc9351bb3e in abort () from /lib/libc.so.6 #2 0x00002adc93550a27 in ?? () from /lib/libc.so.6 #3 0x00002adc93555b1d in ?? () from /lib/libc.so.6 #4 0x00002adc935579b6 in ?? () from /lib/libc.so.6 #5 0x00002adc9355950d in malloc () from /lib/libc.so.6 #6 0x00002adc93177351 in ?? () from /usr/lib64/liblua.so.5 #7 0x00002adc9317add9 in ?? () from /usr/lib64/liblua.so.5 #8 0x00002adc93170237 in lua_getfield () from /usr/lib64/liblua.so.5 #9 0x00002adc938867e9 in ex_spawn (L=0x503010) at ex.c:412 #10 0x00002adc93173fc1 in ?? () from /usr/lib64/liblua.so.5 #11 0x00002adc9317d50e in ?? () from /usr/lib64/liblua.so.5 #12 0x00002adc9317440d in ?? () from /usr/lib64/liblua.so.5 #13 0x00002adc93173b77 in ?? () from /usr/lib64/liblua.so.5 #14 0x00002adc93173bf4 in ?? () from /usr/lib64/liblua.so.5 #15 0x00002adc9316fc75 in lua_pcall () from /usr/lib64/liblua.so.5 #16 0x0000000000401746 in ?? () #17 0x0000000000401b54 in ?? () #18 0x0000000000402137 in ?? () #19 0x00002adc93173fc1 in ?? () from /usr/lib64/liblua.so.5 #20 0x00002adc931743bd in ?? () from /usr/lib64/liblua.so.5 #21 0x00002adc93173b77 in ?? () from /usr/lib64/liblua.so.5 #22 0x00002adc93173bf4 in ?? () from /usr/lib64/liblua.so.5 #23 0x00002adc9316fc17 in lua_cpcall () from /usr/lib64/liblua.so.5 #24 0x000000000040167d in main ()
這是使用標準的 (Gentoo 發布的) Lua 5.1.1。
我編譯了 Lua 5.1.2,結果相同 (更多符號)。
使用 -O0 編譯 Lua 也會導致相同的錯誤。只不過有更多資訊。
#0 0x00002b8a1dc7a885 in raise () from /lib/libc.so.6 #1 0x00002b8a1dc7bb3e in abort () from /lib/libc.so.6 #2 0x00002b8a1dcb0a27 in ?? () from /lib/libc.so.6 #3 0x00002b8a1dcb5b1d in ?? () from /lib/libc.so.6 #4 0x00002b8a1dcb79b6 in ?? () from /lib/libc.so.6 #5 0x00002b8a1dcb950d in malloc () from /lib/libc.so.6 #6 0x0000000000419c8d in l_alloc (ud=0x0, ptr=0x0, osize=0, nsize=29) at lauxlib.c:636 #7 0x000000000040d519 in luaM_realloc_ (L=0x533010, block=0x0, osize=0, nsize=29) at lmem.c:79 #8 0x0000000000411e3b in newlstr (L=0x533010, str=0x2b8a1de8c235 "args", l=4, h=7976507) at lstring.c:56 #9 0x00000000004120ab in luaS_newlstr (L=0x533010, str=0x2b8a1de8c235 "args", l=4) at lstring.c:92 #10 0x00000000004060f4 in lua_getfield (L=0x533010, idx=2, k=0x2b8a1de8c235 "args") at lapi.c:546 #11 0x00002b8a1de8b4be in ex_spawn (L=0x533010) at ex.c:379 #12 0x0000000000409e92 in luaD_precall (L=0x533010, func=0x533410, nresults=0) at ldo.c:319 #13 0x00000000004175b9 in luaV_execute (L=0x533010, nexeccalls=1) at lvm.c:589 #14 0x000000000040a106 in luaD_call (L=0x533010, func=0x533400, nResults=-1) at ldo.c:377 #15 0x0000000000406a30 in f_call (L=0x533010, ud=0x7fff8d573bb0) at lapi.c:796 #16 0x0000000000409192 in luaD_rawrunprotected (L=0x533010, f=0x406a01 <f_call>, ud=0x7fff8d573bb0) at ldo.c:116 #17 0x000000000040a49d in luaD_pcall (L=0x533010, func=0x406a01 <f_call>, u=0x7fff8d573bb0, old_top=48, ef=32) at ldo.c:461 #18 0x0000000000406ad6 in lua_pcall (L=0x533010, nargs=0, nresults=-1, errfunc=1) at lapi.c:817 #19 0x0000000000403dfa in docall (L=0x533010, narg=0, clear=0) at lua.c:100 #20 0x00000000004043d0 in dotty (L=0x533010) at lua.c:219 #21 0x0000000000404b3f in pmain (L=0x533010) at lua.c:367 #22 0x0000000000409e92 in luaD_precall (L=0x533010, func=0x5333e0, nresults=0) at ldo.c:319 #23 0x000000000040a0f4 in luaD_call (L=0x533010, func=0x5333e0, nResults=0) at ldo.c:376 #24 0x0000000000406be0 in f_Ccall (L=0x533010, ud=0x7fff8d573f50) at lapi.c:842 #25 0x0000000000409192 in luaD_rawrunprotected (L=0x533010, f=0x406b11 <f_Ccall>, ud=0x7fff8d573f50) at ldo.c:116 #26 0x000000000040a49d in luaD_pcall (L=0x533010, func=0x406b11 <f_Ccall>, u=0x7fff8d573f50, old_top=16, ef=0) at ldo.c:461 #27 0x0000000000406c37 in lua_cpcall (L=0x533010, func=0x404973 <pmain>, ud=0x7fff8d573fa0) at lapi.c:852 #28 0x0000000000404bb4 in main (argc=1, argv=0x7fff8d5740b8) at lua.c:385
ex.spawn'ls' 語法也會失敗,不過是在 spawn_param_execute 中失敗。
我不了解原因。
傳遞給 realloc 的參數應該是正確的 (gdb 將它們視為 ptr=0x0、nsz=48),但從偵錯輸出中看起來,它卻呼叫了 malloc 取而代之 (這正常嗎?)。在我一般使用 Lua 時不會遇到這種失敗,所以我假設這不是它的錯。
--m.i.
這是已知的錯誤,目前已經修正,但尚未提供。很快就推出!我保證。- MarkEdgar
不知道這一點,我重新實作了大多數功能。這會某種程度地複製程式碼,所以從現在開始,我將簡單地使用您的實作。儘管如此,我仍對 API 進行了一些調整,我认为這會很有用。我在頁面上發布說明,因為說明蠻長的 -- MauroIazzi
我認為可以利用 os.isatty
來讓使用者在「cat myfile | lua myapp.lua -」中避開輸入明確的「-」。Lua 本身會呼叫 isatty,儘管它在 luaconf.h 中 --DavidManura
在這裡可能需要使用全域函式。另請參閱 FileGlob。--DavidManura