Mauro Iazzi

lua-users home
wiki

mailto:mauro.iazzi@gmail.com

Ex API

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


RecentChanges · preferences
编辑 · 历史
最后编辑于2007年7月11日 GMT上午6:40 (差异)