如果命令行上没有指定文件,使用 STDIN 实现标准 Unix 行为的惯用方法?

Idiomatic way to implement standard Unix behaviour of using STDIN if no files are specified on the command line?

是否有更优雅的方式来处理来自命令行参数或 STDIN 如果没有在命令行上提供文件的输入?我目前是这样做的:

sub MAIN(*@opt-files, Bool :$debug, ... other named options ...) {
    # note that parentheses are mandatory here for some reason
    my $input = @opt-files ?? ([~] .IO.slurp for @opt-files) !! $*IN.slurp;

    ... process $input ...
}

还不错,但我想知道我是否缺少一些更简单的方法?

我可能会选择 multi sub MAIN,比如:

multi sub MAIN(Bool :$debug)
{
    process-input($*IN.slurp);
}

multi sub MAIN(*@opt-files, Bool :$debug)
{
    process-input($_.IO.slurp) for @opt-files;
}

我可能会做两件事来改变这一点。我会分手?? !!到不同的行,我会去一个完整的方法链:

sub MAIN(*@opt-files, Bool :$debug, ... other named options ...) {
    my $input = @opt-files 
                  ?? @opt-files».IO».slurp.join
                  !! $*IN.slurp;

    ... process $input ...
}

您也可以使用 @opt-files.map(*.IO.slurp).join

映射它

编辑:在 ugexe's answer 的基础上,您可以做到

sub MAIN(*@opt-files, Bool :$debug, ... other named options ...) {

    # Default to $*IN if not files
    @opt-files ||= '-';

    my $input = @opt-files».IO».slurp.join

    ... process $input ...

}

我可能希望做的事情是将 @*ARGS 设置为签名中的文件名列表。
然后只需使用 $*ARGFILES.

sub MAIN( *@*ARGS, Bool :$debug, ... other named options ...) {

    my $input = slurp; # implicitly calls $*ARGFILES.slurp()

    ... process $input ...
}

不过没用。


您可以让 Rakudo 更新 $*ARGFILES,方法是在您使用它之前用 low-level null 将其清零。

sub MAIN( *@*ARGS, Bool :$debug, ... other named options ...) {

    { use nqp; $*ARGFILES := nqp::null }

    my $input = slurp;

    ... process $input ...
}

但这是使用将来可能会更改的实现细节。


更好的方法是自己直接创建 IO::ArgFiles 的新实例。

您甚至可以将其存储在 $*ARGFILES 中。然后 slurp 自己会吞噬所有文件内容。

sub MAIN( *@opt-files, Bool :$debug, ... other named options ...) {

    my $*ARGFILES = IO::ArgFiles.new( @opt-files || $*IN );

    my $input = slurp;

    ... process $input ...
}

请注意 IO::ArgFiles 只是 IO::CatHandle 的空子类。 所以你可以写 IO::CatHandle.new( @opt‑files || $*IN ) 代替。