Lua的“主要”功能?

时间:2010-12-23 17:30:45

标签: lua

在python中,通常会定义一个main函数,以便允许脚本用作模块(如果需要):

def main():
    print("Hello world")
    return 0

if __name__ == "__main__":
    sys.exit(main())

在Lua中,成语if __name__ == "__main__"是不可能的(这意味着,我认为不是这样)。

这就是我为了在Lua中有类似的行为而经常做的事情:

os.exit((function(args)
    print("Hello world")
    return 0
end)(arg))

......但这种做法在括号上看起来相当“:” - )

是否有更常见的方法(除了定义一个似乎多余的全局主函数)?

8 个答案:

答案 0 :(得分:13)

没有“正确”的方法来做到这一点,因为Lua并没有真正区分代码来自哪里,它们都只是函数。也就是说,这至少似乎在Lua 5.1中有效:

matthew@silver:~$ cat hybrid.lua 
if pcall(getfenv, 4) then
    print("Library")
else
    print("Main file")
end
matthew@silver:~$ lua hybrid.lua 
Main file
matthew@silver:~$ lua -lhybrid
Library
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
> ^C
matthew@silver:~$ lua
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
> require "hybrid"
Library
> ^C
matthew@silver:~$

它的工作原理是检查堆栈深度是否大于3(库存Lua解释器中文件的正常深度)。这个测试可能会破坏Lua版本,甚至在任何嵌入式/自定义Lua版本中也是如此。

我还会包含这个(稍微更便携)的替代方案,虽然它在启发式方面取得了更大的飞跃,并且有一个失败案例(见下文):

matthew@silver:~$ cat hybrid2.lua 
function is_main(_arg, ...)
    local n_arg = _arg and #_arg or 0;
    if n_arg == select("#", ...) then
        for i=1,n_arg do
            if _arg[i] ~= select(i, ...) then
                print(_arg[i], "does not match", (select(i, ...)))
                return false;
            end
        end
        return true;
    end
    return false;
end

if is_main(arg, ...) then
    print("Main file");
else
    print("Library");
end
matthew@silver:~$ lua hybrid2.lua 
Main file
matthew@silver:~$ lua -lhybrid2
Library
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
> ^C
matthew@silver:~$ lua 
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
> require "hybrid2"
Library
>

这个通过比较_G.arg的内容和'...'的内容来工作。在主要块中,它们将始终相同。在模块中,_G.arg仍将包含命令行参数,但“...”将包含传递给require()的模块名称。我怀疑这是更接近更好的解决方案,因为你知道你的模块名称。此代码中的错误在于用户使用1参数执行主脚本时,这就是模块的确切名称:

matthew@silver:~$ lua -i hybrid2.lua hybrid2
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
Main file
> require "hybrid2"
Main file
> 

鉴于上述情况,我希望至少你知道自己的立场,即使它不是你想到的那样:)

更新:对于在Lua 5.1和5.2中运行的hybrid.lua版本,可以使用debug.getlocal替换getfenv:

if pcall(debug.getlocal, 4, 1) then
    print("Library")
else
    print("Main file")
end

答案 1 :(得分:6)

当Lua require是一个模块时,它会以require d的名称将其作为varargs(...)传递给它。

因此,如果您的脚本不打算采用任何参数(从命令行或其他方式),您可以使用类似

的内容
if ... then
  return this_mod --module case
else
  main() --main case
end

但是,请注意,在您采用参数的(完全)可能的情况下,这并非万无一失。但是,此时,您可以将此与Lukasz的答案结合起来:

if not package.loaded[...] then
  --main case
else --module case
end

仍然不完美(例如,如果使用string的第一个参数或其他已经加载的模块的名称调用脚本,但可能已经足够好了。在其他情况下,我会按照MattJ的回答。

答案 2 :(得分:5)

您可以尝试检查是否需要该模块。

来自文档:

  

package.loaded require使用的表   控制哪些模块已经存在   加载。当你需要一个模块   modname和package.loaded [modname]是   不是假的,要求简单地归还   存储在那里的价值。

有了这个,你可以写:

if not package.loaded['modulename'] then
    main()
end

答案 3 :(得分:3)

我将建议另一种变体,这似乎适用于lua5.1以及lua5.2:

function is_main(offset)
    return debug.getinfo(4 + (offset or 0)) == nil
end

if is_main() then
    print("Main chunk!")
else
    print("Library chunk!")
end

如果您不想为此目的定义额外的功能is_main,您可以这样做:

if debug.getinfo(3) == nil then
    print("Main chunk!")
else
    print("Library chunk!")
end

答案 4 :(得分:2)

这有什么问题:

$ cat aa.lua
#!/usr/bin/lua

if (arg ~= nil and arg[-1] ~= nil) then
    print "main"
else
    print "library"
end
$ ./aa.lua
main
$ ./aa.lua arg1 arg2
main
$ cat bb.lua
#!/usr/bin/lua

print("in bb")
$ lua -laa bb.lua
library
in bb
$ lua
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
> require "aa"
library
> 

答案 5 :(得分:1)

if arg ~= nil and arg[0] == string.sub(debug.getinfo(1,'S').source,2) then
  print "Main file"
else
  print "Library"
end

解释:

  1. Lua使用arg表从解释器调用脚本,其中arg[0]是脚本的名称。
  2. debug.getinfo函数返回一个信息表,描述由编号参数给出的堆栈级别的函数(1表示调用getinfo的函数; 0表示getinfo本身)。它的第二个参数是可选的:它指定要检索的getinfo字段的哪个子集。 'S'定义 S 源名称。
  3. source为文件返回的表的getinfo字段包含定义函数的文件的名称,前缀为@。通过将该字段的值传递给string.sub(...,2),我们将@删除。 (short_src字段包含不带@的名称,但这是因为它被定义为“打印友好”名称,并且不如剥离source安全。)
  4. 请注意,此代码使用debug函数,因此在5.2中您必须require debug才能使用它。

答案 6 :(得分:1)

也许您可以使用 debug.getinfo()函数来处理调试库

if debug.getinfo(1).what == "main" then
    -- Main execution
end

有关详细信息,请参阅参考手册。

答案 7 :(得分:0)

我正在使用lua 5.3并且在这里遇到了大多数建议的问题。这对我的用例有用:

local my_module = {}
...
if os.getenv('CLI') then
  main()
else
  return my_module
end

从命令行运行时,我简单地定义了环境变量,例如:

CLI=1 lua my_script.lua

为我工作™