«главная» функция в Lua? - PullRequest
22 голосов
/ 23 декабря 2010

В python обычно нужно определить основную функцию, чтобы позволить сценарию использоваться в качестве модуля (при необходимости):

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 ]

13 голосов
/ 23 декабря 2010

Нет «правильного» способа сделать это, так как 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
> 

Учитывая вышесказанное, я надеюсь, что, по крайней мере, вы знаете, где вы находитесь, даже если это не совсем то, что вы имели в виду:)

Обновление: Для версии hybrid.lua, которая работает в Lua 5.1 и 5.2, вы можете заменить getfenv на debug.getlocal:

if pcall(debug.getlocal, 4, 1) then
    print("Library")
else
    print("Main file")
end
6 голосов
/ 17 мая 2011

Когда Lua require s модуль, он передает ему имя, с которым он был require d, как varargs (...).

Итак, если ваш скрипт не намерен принимать какие-либо аргументы (из командной строки или иным образом), вы можете использовать что-то вроде

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

Обратите внимание, однако, что это не надежно в (полностью) возможном случае, когда вы принимаете аргументы. Однако в этот момент вы можете объединить это с ответом Лукаша, чтобы получить:

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

Все еще не идеально (например, если скрипт вызывается с первым аргументом string или именем какого-либо другого уже загруженного модуля), но, вероятно, достаточно хорош. В других ситуациях я откладываю ответ Мэтта.

5 голосов
/ 23 декабря 2010

Вы можете попытаться проверить, был ли модуль необходим.

из документации:

package.loaded Таблица, используемая require для управления тем, какие модули уже загружены.Когда вам требуется модуль modname и package.loaded [modname] не имеет значения false, require просто возвращает сохраненное там значение.

С этим вы можете написать:

if not package.loaded['modulename'] then
    main()
end
3 голосов
/ 11 сентября 2013

Я собираюсь предложить еще один вариант, который работает на 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
2 голосов
/ 19 февраля 2012

Что не так с этим:

$ 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
> 
1 голос
/ 20 февраля 2012

Может быть, вы можете просто разобраться с библиотекой отладки, с функцией debug.getinfo ()

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

См. Справочное руководство для получения дополнительной информации.

1 голос
/ 17 мая 2011
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.)

Обратите внимание, что в этом коде используется функция debug, поэтому в 5.2 вам потребуется require debug, чтобы иметь возможность его использовать.

0 голосов
/ 05 августа 2016

Я использую lua 5.3 и у меня были проблемы с большинством предложений здесь. Вот что сработало для моего варианта использования:

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

При запуске из командной строки я просто определил переменную среды, такую ​​как:

CLI=1 lua my_script.lua

У меня работает ™

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...