在函数中§
查看主要文档 在上下文中 了解 sub MAIN
在 Raku 脚本中声明 sub MAIN
不是强制性的,但你可以提供一个来为你的脚本创建一个 命令行界面。
在命令行界面中§
查看主要文档 在上下文中 了解 sub MAIN
在所有相关的入口阶段(BEGIN
、CHECK
、INIT
、PRE
、ENTER
)运行后,并且脚本的 主线 已经执行后,将执行具有特殊名称 MAIN
的子例程。如果没有 MAIN
子例程,则不会发生错误:你的脚本只需要在脚本的主线中完成工作,例如解析参数。
从 MAIN
子例程的任何正常退出都将导致退出代码 0
,表示成功。将忽略 MAIN
子例程的任何返回值。如果抛出一个未在 MAIN
子例程中处理的异常,则退出代码将为 1
。如果分派到 MAIN
失败,则会在 STDERR 上显示一条用法消息,并且退出代码将为 2
。
命令行参数存在于 @*ARGS
动态变量中,并且可以在调用 MAIN
单元之前在脚本的主线中进行更改。
(多重)子例程 MAIN
的签名决定了使用标准 多重分派 语义实际调用哪个候选者。
一个简单的示例
# inside file 'hello.raku'sub MAIN()
如果你在没有任何参数的情况下调用该脚本,你将得到以下用法消息
$ raku hello.raku
Usage:
hello.raku <name>
但是,如果你为参数给定一个默认值,则无论是否指定名称,运行脚本始终有效
# inside file 'hello.raku'sub MAIN( = 'bashful')
$ raku hello.raku
Hello bashful, how are you?
$ raku hello.raku Liz
Hello Liz, how are you?
另一种方法是将 sub MAIN
设置为 multi
# inside file 'hello.raku'multi MAIN()multi MAIN()
这将给出与上面示例相同的结果。你应该使用哪种方法来实现所需的目标完全取决于你。
如果你想传递不确定的数量的参数在 sub MAIN
中处理,你可以使用 slurpy 参数
# inside file 'hello-all.raku'sub MAIN(*)
$ raku hello-all.raku peter paul mary
Hello, peter
Hello, paul
Hello, mary
一个更复杂的示例,使用单个位置参数和多个命名参数,还显示了 where
子句也可以应用于 MAIN
参数
# inside "frobnicate.raku"sub MAIN(Str where *.IO.f = 'file.dat',Int : = 24,Bool :)
当 file.dat
存在时,它将这样工作
$ raku frobnicate.raku
24
file.dat
Verbosity off
或者使用 --verbose
这样工作
$ raku frobnicate.raku --verbose
24
file.dat
Verbosity on
如果文件 file.dat
不存在,或者你指定了另一个不存在的文件名,你将获得从 MAIN
子例程的自省中创建的标准用法消息
$ raku frobnicate.raku doesnotexist.dat
Usage:
frobnicate.raku [--length=<Int>] [--verbose] [<file>]
尽管你不需要在代码中执行任何操作来执行此操作,但它仍然可能被认为有点简洁。但是,可以通过使用 pod 特性提供提示,轻松地改善该用法消息
# inside "frobnicate.raku"sub MAIN(Str where *.IO.f = 'file.dat', #= an existing file to frobnicateInt : = 24, #= length needed for frobnicationBool :, #= required verbosity)
这将像这样改进用法消息
$ raku frobnicate.raku doesnotexist.dat
Usage:
frobnicate.raku [--length=<Int>] [--verbose] [<file>]
[<file>] an existing file to frobnicate
--length=<Int> length needed for frobnication
--verbose required verbosity
从 2021.03 版本开始,传递给单个命名参数的值也可以用空格分隔。考虑一个具有以下源代码的 demo
程序
of Any where Str|True;of Str;multi MAIN(,name :, #= Write profile information to a fileport :, #= Listen for debugger connections on the specified portBool :v(), #= Display verbose output)multi MAIN("--process-files", *)
该程序生成以下用法消息
Usage: demo [--profile[=name]] [--debug-port=<port>] [-v] <file> demo --process-files [<images> ...] --profile[=name] Write profile information to a file --debug-port=<port> Listen for debugger connections on the specified port -v Display verbose output
以下为调用 demo
的有效方法
demo --profile ~/foo demo --profile=/tmp/bar ~/foo demo --debug-port 4242 ~/foo demo --debug-port=4242 ~/foo demo -v ~/foo demo --process-files *.jpg
但是,以下方法无效
demo --profile /tmp/bar ~/foo demo --debug-port ~/foo
第一个方法无效,因为 /tmp/bar
和 ~/foo
均被解析为位置参数,这意味着 demo
使用了过多的位置参数进行调用。第二个方法无效,因为 ~/foo
被解析为 --debug-port
的参数,因此 demo
缺少必需的位置参数。
以下是其工作原理;Raku 区分三种类型的选项
布尔选项(如
-v
),从不带参数;它们要么存在,要么不存在。具有必需参数的选项(如
--debug-port
),始终带参数。如果您使用=
为其提供参数,它将使用该参数;如果没有,它将使用以下参数。具有可选参数的选项(如
--profile
),在带参数和不带参数的情况下均有效。您只能使用=
语法为这些参数提供选项;如果该选项后有空格,则表示它在未带参数的情况下被调用。
以下是生成每种类型参数的签名
与任何其他子例程一样,MAIN
可以为其命名参数定义 别名。
sub MAIN(Str where *.IO.f = 'file.dat', #= an existing file to frobnicateInt :size(:) = 24, #= length/size needed for frobnicationBool :, #= required verbosity)
在这种情况下,这些别名也将作为备选方案与 --help
一起列出
Usage: frobnicate.raku [--size|--length=<Int>] [--verbose] [<file>] [<file>] an existing file to frobnicate --size|--length=<Int> length needed for frobnication --verbose required verbosity
Enumeration
可用于签名,其中参数会自动转换为其对应的 enum
符号
(FLAG_FOO => 0b001,FLAG_BAR => 0b010,FLAG_BAZ => 0b100,);sub MAIN(Flag = FLAG_FOO)
这将与以下内容正确配合使用
raku MAIN-enum.raku FLAG_BAR
但如果使用并非 Flag
的内容进行调用,则会终止。