“main” function in Lua?

前端 未结 8 754
醉酒成梦
醉酒成梦 2020-12-01 12:17

In python, one would usually define a main function, in order to allow the script to be used as module (if needed):

def main():
    print(\"Hello world\")
           


        
相关标签:
8条回答
  • 2020-12-01 12:41

    What's wrong with this:

    $ 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
    > 
    
    0 讨论(0)
  • 2020-12-01 12:43

    There's no "proper" way to do this, since Lua doesn't really distinguish code by where it came from, they are all just functions. That said, this at least seems to work in 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:~$
    

    It works by checking whether the stack depth is greater than 3 (the normal depth for a file in the stock Lua interpreter). This test may break between Lua versions though, and even in any embedded/custom Lua builds.

    I'll also include this (slightly more portable) alternative, although it's taking an even greater leap in heuristics, and has a failure case (see below):

    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
    >
    

    This one works by comparing the contents of _G.arg with the contents of '...'. In the main chunk they will always be the same. In a module _G.arg will still contain the command-line arguments, but '...' will contain the module name passed to require(). I suspect this is closer to the better solution for you, given that you know your module name. The bug in this code lies when the user executes the main script with 1 argument, and this is the exact name of your module:

    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
    > 
    

    Given the above I hope at least you know where you stand, even if it isn't exactly what you had in mind :)

    Update: For a version of hybrid.lua that works in Lua 5.1 and 5.2, you can replace getfenv with debug.getlocal:

    if pcall(debug.getlocal, 4, 1) then
        print("Library")
    else
        print("Main file")
    end
    
    0 讨论(0)
  • 2020-12-01 12:47

    you could try to check if the module has been required.

    from documentation:

    package.loaded A table used by require to control which modules are already loaded. When you require a module modname and package.loaded[modname] is not false, require simply returns the value stored there.

    With this you could write:

    if not package.loaded['modulename'] then
        main()
    end
    
    0 讨论(0)
  • 2020-12-01 12:55

    Maybe you can just deal with the debug library, with the debug.getinfo() function

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

    See the reference manual for more information.

    0 讨论(0)
  • 2020-12-01 12:56

    When Lua requires a module, it passes it the name it's been required with as varargs (...).

    So, if your script doesn't intend to take any arguments (from the command line or otherwise), you can use something like

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

    Note, however, that this isn't foolproof in the (entirely) possible case that you take arguments. However, at this point, you can combine this with Lukasz's answer to get:

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

    Still not perfect (for instance, if the script is called with a first argument of string or the name of some other already-loaded module), but likely good enough. In other situations, I defer to MattJ's answer.

    0 讨论(0)
  • 2020-12-01 12:58

    I'm using lua 5.3 and had issues with most of the suggestions here. This is what worked for my use case:

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

    When running from the command line I simple defined the environment variable such as:

    CLI=1 lua my_script.lua
    

    Works for me™

    0 讨论(0)
提交回复
热议问题