Mauro Iazzi |
|
关于ExtensionProposal,我在重新实现时有一些想法。拥有两个独立的API实现是没有意义的,所以我把代码[放在这里],如果你想看的话。它符合POSIX.1-2001标准,但不确定其他标准(当然不是之前的POSIX)。
提案的是API,而不是实现。我的实现仅仅是作为参考实现。所以,你不需要说/重/实现。:) 我不认为拥有同一个API的多个实现是没有意义的。-Mark
只要你继续维护,你的代码可能比我的好。无论如何,提出的更改是针对API的... --mi
我实现了它们,尽可能简单,可能和Mark的方式一样。
* getenv, * setenv, * environ, * chdir, * mkdir (setting perms to 0777, plus implied umask), * currentdir
无论如何,接口是一样的,所以Mark的代码肯定更好。一些其他函数甚至没有被复制,因为我不需要它们。
"lock"函数的工作方式和Mark的一样,但它接受一个'u'模式来解锁。
function ex.unlock = function (file, offset, length) return ex.lock(file, 'u', offset, length) end我认为这是品味问题。
我不明白为什么ex不能在lock函数中允许一个"u"模式字符串。参考实现已经允许了。事实上,甚至可以只有lock函数。-Mark
我不知道,所以这个也是一样的。--mi
我添加了一个getid函数。
local uid, euid, gid, egid = ex.getid()
这个怎么样,可能有用--mi
我在优化方面遇到了一些问题,所以我实现了"opendir"、"closedir"和"readdir",它们只是对相应的POSIX函数的封装。
local d = assert(ex.opendir(name)) for filename in ex.readdir, d do print(filename) --- you can even break out of the loop if (string.match(filename, pattern)) then break end end ex.closedir(d)
Mark的ex.dir可以用这些函数像这样模拟:
function ex.dir (name) local d = assert(ex.opendir(name)) return function () local f = ex.readdir(d) if (f)==nil then ex.closedir(d) end return f end end for fn in ex.dir(name) do print(fn) -- you cannot break or dir will remain open until collected end
我认为这更灵活,因为你可以多次遍历目录,可以跳出循环并关闭目录,所以如果你在一个*巨大的*目录中搜索一个文件,并且很快就找到了,你可以节省很多时间。你甚至可以重用目录,使用我封装的"rewinddir"。实现也像这样非常简单,一个C函数对应一个Lua函数。
local d = assert(ex.opendir(name)) local name = "first" while name ~= "last" do ex.rewinddir(d) for filename in ex.readdir, d do print(filename) --- you can even break out of the loop if (filename == name) then local f = assert(io.open(filename)) name = f:read('*l') f:close() break end end end ex.closedir(d)
此外,readdir只返回文件名,但我实现了一个fstat(和lstat),它返回一些关于文件的信息。
我导出了stat和lstat。它们也只是封装。我认为像这样划分函数更简单,而且名称比"dirent"更直观。
所以,ex不直接导出开放/读取/关闭风格的API来进行目录迭代的原因是,迄今为止最常见的用法是在循环中迭代。如果一个人确实需要部分地迭代很多很多目录(在这种情况下,收集可能不够快),那么你仍然可以通过os.dir()来实现,尽管它在API中没有完全说明。-Mark
好的,我不知道。所以我想,在Lua中实现API的一部分也是可以的,是吗?--mi
d,ds,di = assert(os.dir(name)) for entry in d,ds,di do if name==entry.name then break end end d.getmetatable().__gc(d)
我只担心如果这成为API要求,它会极大地限制实现...例如,我坚持使用迭代器函数,不透明的数据模型,这用于普通表(对于k, v in next, t do ... end)。我的迭代器是一个简单的函数,没有单独的元表,所以我无法提供一个方法来使这段代码工作。另一方面,API应该实际指定一个显式关闭目录的方法...我没有头绪。--mi
话虽如此,我认为有opendir/readdir/closedir并非坏事。有stat和lstat也并非坏事。但是,你仍然需要实现ex.dir和ex.dirent。dirent可能是一个糟糕的名字,但虽然stat可能更直观(至少对UNIX程序员来说),我不想混淆ex和POSIX。你对这个函数的名称有什么更好的建议吗?-Mark
我同意,我的命名确实严格基于POSIX,因为对我来说也只是如此。我认为ex.fileinfo将完全符合Lua的命名风格。至于fstat - lstat的区别,你有什么想法吗?--mi
请注意,如果你真正想要的是一个POSIX API,那么你应该使用lhf的POSIX封装http://www.tecgraf.puc-rio.br/~lhf/ftp/lua/#lposix,而不是ex。-Mark
''呵呵,但似乎他把这个提案的负担留给了我,我在遵循5.1实现的链接时发现:https://lua-users.lua.ac.cn/lists/lua-l/2006-10/msg00499.html 顺便说一句,我完全同意这不应该仅仅是一个POSIX封装,并且我支持更改名称,首先。--mi''
那么"spawn":这是大的改变。我定义它很简单,基本上。它像这段Lua代码一样使用参数:
function lua_spawn(t) if type(t)=='string' then t = { t } end assert(type(t)=='table') -- "spawn must receive a table or a string" local argv = t.argv or t local argc = #argv local command = t.command or argv[1] or error'command not given' if not argv then argv = { command } end local env = t.env or ( type(t.env)==nil and ex.environ() or {} ) if t.stdin then set_redirect_to(redirections, t.stdin) end if t.stderr then set_redirect_to(redirections, t.stderr) end if t.stdout then set_redirect_to(redirections, t.stdout) end return posix_spawn(command, argv, argc, env, redirections) end它像这样使用(它使用argv而不是args):
proc = ex.spawn"/bin/echo" proc = ex.spawn{"/bin/echo", "hello", "world"} proc = ex.spawn{command="/bin/echo", "echo", "hello", "world"} --big difference proc = ex.spawn{argv = {"lua", "-e", 'print"Hello world\n"'}} -- equivalent to Mark's example proc = ex.spawn{"lua", "-e", 'print"Hello world\n"'} proc = ex.spawn{"lua", "-e", 'print"Hello world\n"', env=ex.environ()} proc = ex.spawn{"/usr/bin/lua", "-e", 'print"Hello world\n"', env=false} -- this uses a void env proc = ex.spawn{"/usr/bin/lua", "-e", 'print"Hello world\n"', env="equivalent"} -- this uses a void env proc = ex.spawn{command='/usr/bin/vim', "ex", "ex.c"} -- starts vim in "ex" mode proc = ex.spawn{command='/bin/busybox', argv = {"ls", "ex.c" }} -- ls ex.c proc = ex.spawn{command='/bin/busybox', argv = {"cp", "ex.c", 'ex2.c'}} -- cp ex.c ex2.c, same binary proc = ex.spawn{"echo", "hello", "world", env={ PATH="/bin" } } proc = ex.spawn{"echo", "hello", "world", env={ "PATH=/bin" } } -- same as before请注意,如果你将env置空,PATH搜索将不起作用。
为什么是argv而不是args?-Mark
因为我很笨,写这个的时候没记住你的名字。也从来没想过要改。这个本来只是给自己用的,我展示出来只是为了测试我如何理解API规范。它不是一个“改变你API的参考实现”之类的东西……只是普通的代码,写的时候我想到了一些东西。其中大部分是错的,你看到了 :) --mi
PID userdata随后可以被waitpid(pid)消耗,这也是一个方法:
ex.waitpid(pid) pid:waitpid()并返回两个值,一个代表进程是退出、被杀死还是(我记不清的另一种选项),另一个分别是退出代码、杀死信号或(...我必须再看看我写了什么)。
为什么是waitpid而不是wait?wait()应该可以返回退出代码和第二个返回值以及额外信息。-Mark
我坚持使用POSIX名称,并想弄清楚这和你的不一样。它将退出代码作为第二个参数返回,所以不兼容。如果其他信息被返回,它应该是第二个返回值而不是第一个,以符合你的API --mi
这似乎很容易使用,并且主要与Mark的*不同*之处在于,env可以是调用环境或voided,并且指定命令和argv必须使用额外的argv[1]字符串。我认为这是可以接受的,因为如果你需要对spawn参数进行如此多的控制,你可以仔细检查它,并且可能希望能够通过路径调用程序并使用不同的第0个参数(例如vim会注意这一点)。
我不确定你说的“voided”环境是什么意思。你是说一个空环境,还是一个继承的环境?ex已经允许了这两种情况。另外请注意,ex API确实支持零个参数,无论是否有'args'。
''关于env是真的,我误读了提案,我很抱歉。至于零个参数,我觉得命令字段的存在如何解释args列表有点反直觉。特别是,我比较了:
os.spawn{"echo", "hello", "world"} os.spawn{command="/bin/echo", [0]="echo", "hello", "world"}我将这解释为对陈述的两个更改(我的意思是,当我思考这个变化时,这就是我的看法)。如果命令字段不为我提供任何我能在不使用[0]=的情况下做的事情,那么为什么它很有用?它看起来只是一个便利,似乎不应该进入API本身,因为任何人都可以基于它来实现。而且,这意味着同一个args表可以在两种语法中无缝使用,无需偏移。如果(像我一样)你用它来自动化处理大量文件,这可能会很有用。--mi''
os.spawn{command="/bin/echo", [0]="echo", "hello", "world"} os.spawn{command="/usr/bin/vim", [0]="ex", "ex.c"} os.spawn{command="/bin/busybox", args={[0]="ls", "ex.c"}} os.spawn{command="/bin/busybox", args={[0]="cp", "ex.c", "ex2.c"}}
-Mark