这将查看 中的值,根据某些策略解释这些值,并创建一个 对象。解析器的替代方式可以由开发者提供或使用模块安装。

    使用该捕获调用提供的MAIN子例程

    标准多用于使用生成的 Capture 对象调用 MAIN 子例程。这意味着您的 MAIN子 例程可能是一个 multi sub,其中每个候选程序负责处理给定命令行参数的某些部分。

    如果调用 MAIN 失败,则创建/显示使用信息

    如果多重分派失败,则应尽可能通知脚本的用户失败的原因。默认情况下,这是通过检查每个 MAIN 候选 sub 的签名以及任何关联的 pod 信息来完成的。然后在 STDERR 上向用户显示结果(如果指定了 --help,则在 STDOUT 上显示)。生成使用信息的替代方式可以由开发者提供或使用模块安装。

    sub MAIN

    在运行所有相关的输入phasers(BEGINCHECKINITPREENTER)并执行脚本的主线之后,将执行具有特殊名称 MAIN 的子程序。如果没有 MAIN sub,则不会发生错误:您的脚本只需要在脚本的主线中执行工作,例如参数解析。

    从 MAIN sub 的任何正常退出将导致退出代码为 0,表示成功。 MAIN 子的任何返回值都将被忽略。如果抛出未在 MAIN 子内部处理的异常,则退出代码将为 1。如果调度到 MAIN 失败,则在 STDERR 上将显示一条用法消息,退出代码将为 2。

    命令行参数存在于 @*ARGS 动态变量中,并且可以在调用 MAIN 单元之前在脚本的主线中进行更改。

    (多个子 MAIN 的候选者)的签名确定使用标准多重分派语义实际调用哪个候选者。

    一个简单的例子:

    如果您调用该脚本没有任何参数:

    1. $ raku hello.p6
    2. Usage:
    3. hello.p6 <name>

    但是,如果为参数指定默认值,则无论是否指定名称,运行脚本始终有效:

    1. # inside file 'hello.p6'
    2. sub MAIN($name = 'bashful') {
    3. say "Hello $name, how are you?"
    4. }
    1. $ raku hello.p6
    2. Hello bashful, how are you?
    1. $ raku hello.p6 Liz
    2. Hello Liz, how are you?

    另一种方法是使 sub MAIN 成为一个 multi sub

    1. # inside file 'hello.p6'
    2. multi sub MAIN() { say "Hello bashful, how are you?" }
    3. multi sub MAIN($name) { say "Hello $name, how are you?" }

    这将提供与上述示例相同的输出。您是否应该使用任何一种方法来实现预期目标完全取决于您。

    使用单个位置和多个命名参数的更复杂的示例:

    1. # inside "frobnicate.p6"
    2. sub MAIN(
    3. Str $file where *.IO.f = 'file.dat',
    4. Int :$length = 24,
    5. Bool :$verbose
    6. ) {
    7. say $length if $length.defined;
    8. say $file if $file.defined;
    9. say 'Verbosity ', ($verbose ?? 'on' !! 'off');
    10. }

    有了 file.dat,这将以这种方式工作:

    1. $ raku frobnicate.p6
    2. 24
    3. file.dat
    4. Verbosity off

    如果文件 file.dat 不存在,或者您指定了另一个不存在的文件名,您将获得从 MAIN 子的内省创建的标准用法消息:

    1. $ raku frobnicate.p6 doesntexist.dat
    2. Usage:

    虽然您不必在代码中执行任何操作,但它仍然可能被视为有点简洁。但是通过使用 pod 功能提供提示,有一种简单的方法可以更好地使用该消息:

    1. # inside "frobnicate.p6"
    2. sub MAIN(
    3. Str $file where *.IO.f = 'file.dat', #= an existing file to frobnicate
    4. Int :$length = 24, #= length needed for frobnication
    5. Bool :$verbose, #= required verbosity
    6. ) {
    7. say $length if $length.defined;
    8. say $file if $file.defined;
    9. say 'Verbosity ', ($verbose ?? 'on' !! 'off');
    10. }

    哪个会改善这样的用法消息:

    1. $ raku frobnicate.p6 doesntexist.dat
    2. frobnicate.p6 [--length=<Int>] [--verbose] [<file>]
    3. [<file>] an existing file to frobnicate
    4. --length=<Int> length needed for frobnication
    5. --verbose required verbosity

    通过设置 %*SUB-MAIN-OPTS 哈希中的选项,可以在将参数传递给 sub MAIN {} 之前更改参数的处理方式。由于动态变量的性质,需要设置 %*SUB-MAIN-OPTS 哈希并使用适当的设置填充它。例如:

    1. my %*SUB-MAIN-OPTS =
    2. :named-anywhere, # allow named variables at any location
    3. # other possible future options / custom options
    4. ;
    5. sub MAIN ($a, $b, :$c, :$d) {
    6. say "Accepted!"
    7. }

    可用选项包括:

    named-anywhere

    默认情况下,传递给程序的命名参数(即 MAIN)在任何位置参数后都不会出现。但是,如果将 %*SUB-MAIN-OPTS<named-anywhere> 设置为 true 值,则可以在任何位置指定命名参数,即使在位置参数之后也是如此。例如,可以使用以下命令调用上述程序:

    1. $ raku example.p6 1 --c=2 3 --d=4

    is hidden-from-USAGE

    有时您希望排除MAIN候选者显示在任何自动生成的使用消息中。这可以通过向您不想显示的 MAIN 候选者的规范添加 hidden-from-USAGE 特征来实现。扩展前面的例子:

    1. # inside file 'hello.p6'
    2. multi sub MAIN() is hidden-from-USAGE {
    3. say "Hello bashful, how are you?"
    4. }
    5. multi sub MAIN($name) { #= the name by which you would like to be called
    6. say "Hello $name, how are you?"
    7. }

    因此,如果您只使用命名变量调用此脚本,您将获得以下用法:

    1. $ raku hello.p6 --verbose
    2. Usage:
    3. hello.p6 <name> -- the name by which you would like to be called

    没有第一个候选者 hidden-from-USAGE 特征,它看起来像这样:

    虽然技术上是正确的,但也不能读。

    MAIN 的单位作用域定义

    如果整个程序体驻留在 MAIN 中,则可以使用单位声明符,如下所示(调整前面的示例):

    1. unit sub MAIN(
    2. Str $file where *.IO.f = 'file.dat',
    3. Int :$length = 24,
    4. Bool :$verbose,
    5. ); # <- note semicolon here
    6. say $length if $length.defined;
    7. say $file if $file.defined;
    8. say 'Verbosity ', ($verbose ?? 'on' !! 'off');
    9. # rest of script is part of MAIN

    请注意,这只适用于只有一个(仅)sub MAIN 的情况。

    如果找不到给定命令行参数的 MAIN 的多候选者,则调用 sub USAGE。如果未找到此类方法,编译器将输出默认用法消息。

    1. multi MAIN(Int $i) { say $i == 42 ?? 'answer' !! 'dunno' }
    2. #|(divide two numbers)
    3. multi MAIN($a, $b){ say $a/$b }
    4. sub USAGE() {
    5. print Q:c:to/EOH/;
    6. Usage: {$*PROGRAM-NAME} [number]
    7. Prints the answer or 'dunno'.
    8. EOH
    9. }

    通过只读 $*USAGE 变量,sub USAGE 内的默认用法消息可用。它将基于可用的 sub MAIN 候选者及其参数生成。如前所示,您可以使用 #|(…​) Pod 块为每个候选项指定其他扩展描述以设置 WHY

    拦截 CLI 参数解析(2018.10, v6.d and later)

    sub ARGS-TO-CAPTURE

    ARGS-TO-CAPTURE 子程序应该接受两个参数:一个 表示要执行的 MAIN 单元(因此可以在必要时进行内省)和一个带有来自命令行的参数的数组。它应该返回一个将用于调度 MAIN 单元的 Capture 对象。一个非常人为的例子,它将根据输入的某个关键字创建一个 Capture(在测试脚本的命令行界面时可以很方便):

    1. sub ARGS-TO-CAPTURE(&main, @args --> Capture) {
    2. # if we only specified "frobnicate" as an argument
    3. @args == 1 && @args[0] eq 'frobnicate'
    4. # then dispatch as MAIN("foo","bar",verbose => 2)
    5. ?? Capture.new( list => <foo bar>, hash => { verbose => 2 } )
    6. # otherwise, use default processing of args
    7. !! &*ARGS-TO-CAPTURE(&main, @args)
    8. }

    请注意,动态变量 可用于执行捕获处理的默认命令行参数,因此如果您不想,则不必重新发明整个轮子。

    拦截使用消息生成(2018.10,v6.d及更高版本)

    您可以通过自己提供 GENERATE-USAGE 子例程,或者从生态系统中可用的任何 模块导入一个子例程来替换或扩充默认的使用方式消息生成方式(在向 MAIN 发送失败之后)。

    sub RUN-MAIN

    定义为:

    1. sub RUN-MAIN(&main, $mainline, :$in-as-argsfiles)

    该程序允许完全控制 MAIN 的处理。它得到一个 Callable,它是应该执行的 MAIN,主线执行的返回值和其他命名变量

    in-as-argsfiles 如果 STDIN 应该被视为 $*ARGFILES,它将为 True

    如果未提供 RUN-MAIN,将运行默认的 RUN-MAIN 以查找旧接口的子例程,例如 MAIN_HELPERUSAGE。如果找到,将执行“旧”语义。

    1. class Hero {
    2. has @!inventory;
    3. has Str $.name;
    4. submethod BUILD( :$name, :@inventory ) {
    5. $!name = $name;
    6. @!inventory = @inventory
    7. }
    8. }
    9. sub new-main($name, *@stuff ) {
    10. Hero.new(:name($name), :inventory(@stuff) ).perl.say
    11. }
    12. RUN-MAIN( &new-main, Nil );

    这将打印生成的对象的名称(第一个参数)。

    GENERATE-USAGE 子例程应该接受一个 Callable,表示由于调度失败而未执行的 MAIN 子例程。这可以用于内省。所有其他参数都是设置为发送到MAIN的参数。它应该返回您想要显示给用户的使用信息的字符串。这个例子只是重新创建从处理参数创建的 Capture

    1. sub GENERATE-USAGE(&main, |capture) {
    2. capture<foo>:exists
    3. ?? "You're not allowed to specify a --foo"
    4. !! &*GENERATE-USAGE(&main, |capture)
    5. }

    您还可以使用 multi 子例程来创建相同的效果:

    1. multi sub GENERATE-USAGE(&main, :$foo!) {
    2. "You're not allowed to specify a --foo"
    3. }
    4. multi sub GENERATE-USAGE(&main, |capture) {
    5. &*GENERATE-USAGE(&main, |capture)
    6. }

    请注意,动态变量 &*GENERATE-USAGE 可用于执行默认使用消息生成,因此您不必重新发明整个轮子。

    拦截 MAIN 调用(2018.10之前,v6.e)

    较旧的接口使得一个接口完全拦截对 MAIN 的调用。这取决于是否存在 MAIN_HELPER 子程序,如果在程序的主线中找到 MAIN 子程序,则该子程序将被调用。

    此接口从未记录过。但是,使用此未记录的界面的任何程序将继续运行,直到 v6.e。从 v6.d 开始,使用未记录的 API 将导致 DEPRECATED 消息。

    生态系统模块可以提供新旧接口,以便与旧版本的 Raku 兼容:如果较新的 Raku 识别出新的(记录的)接口,它将使用它。如果没有可用的新接口子例程,但旧的 MAIN_HELPER 接口是,那么它将使用旧接口。