The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Perinci::CmdLine::Manual::FAQ - FAQs

VERSION

This document describes version 0.65 of Perinci::CmdLine::Manual::FAQ (from Perl distribution Perinci-CmdLine-Lite), released on 2014-12-18.

NOMENCLATURE

[Full] denotes that the answer applies to Perinci::CmdLine only and not Perinci::CmdLine::Lite. [Lite] denotes that the answer is specific to Perinci::CmdLine::Lite. [Any] denotes that the answer is specific to Perinci::CmdLine::Any.

GENERAL

How does P::C compare with other CLI-app frameworks?

The main difference is that Perinci::CmdLine accesses your code through Riap protocol, not directly. This means that aside from local Perl code, P::C can also provide CLI for code in remote hosts/languages. For a very rough demo, download and run this PHP Riap::TCP server https://github.com/sharyanto/php-Phinci/blob/master/demo/phi-tcpserve-terbilang.php on your system. After that, try running:

 % peri-run riap+tcp://localhost:9090/terbilang --help
 % peri-run riap+tcp://localhost:9090/terbilang 1234

Everything from help message, calling, argument checking, tab completion works for remote code as well as local Perl code.

My application is OO?

This framework is currently non-OO and function-centric. There are already several OO-based command-line frameworks on CPAN.

DEBUGGING

How do I debug my program?

[Full] You can set environment DEBUG=1 or TRACE=1. See Log::Any::App for more details.

INPUT

How to accept input from STDIN (or files)?

If you specify 'cmdline_src' to 'stdin' to a 'str' argument, the argument's value will be retrieved from standard input if not specified. Example:

 use Perinci::CmdLine;
 $SPEC{cmd} = {
     v => 1.1,
     args => {
         arg => {
             schema => 'str*',
             cmdline_src => 'stdin',
         },
     },
 };
 sub cmd {
     my %args = @_;
     [200, "OK", "arg is '$args{arg}'"];
 }
 Perinci::CmdLine->new(url=>'/main/cmd')->run;

When run from command line:

 % cat file.txt
 This is content of file.txt
 % cat file.txt | cmd
 arg is 'This is content of file.txt'

If your function argument is an array, array of lines will be provided to your function. A mechanism to be will be provided in the future (currently not yet specified in Rinci::function specification).

But I don't want the whole file content slurped into string/array, I want streaming!

Riap 1.2 defines streaming input (and output), which P::C supports to some degree. If you set your argument property with stream then local input streaming will work. Argument retrieved from file/stdin will be presented to function as filehandle, which the function can then read data from.

Currently, see App::dux for an example on how to accomplish this on function argument of type array. Basically in App::dux, you feed an array tied with Tie::Diamond as a function argument. Thus you can get lines from file/STDIN iteratively with each().

My function has some cmdline_aliases or cmdline_src defined but I want to change it!

For example, your f1 function metadata might look like this:

 package Package::F1;
 our %SPEC;
 $SPEC{f1} = {
     v => 1.1,
     args => {
         foo => {
             cmdline_aliases => { f=> {} },
         },
         bar => { ... },
         fee => { ... },
     },
 };
 sub f1 { ... }
 1;

And your command-line script f1:

 #!perl
 use Perinci::CmdLine;
 Perinci::CmdLine->new(url => '/Package/F1/f1')->run;

Now you want to create a command-line script interface for this function, but with -f as an alias for --fee instead of --foo. This is best done by modifying the metadata and creating a wrapper function to do this, e.g. your command-line script f1 becomes:

 package main;
 use Perinci::CmdLine;
 use Package::F1;
 use Data::Clone;
 our %SPEC;
 $SPEC{f1} = clone $Package::F1::SPEC{f1};
 delete $SPEC{f1}{args}{foo}{cmdline_aliases};
 $SPEC{f1}{args}{fee}{cmdline_aliases} = {f=>{}};
 *f1 = \&Package::F1::f1;
 Perinci::CmdLine->new(url => '/main/f1')->run;

This also demonstrates the convenience of having the metadata as a data structure: you can manipulate it however you want. There is also a convenient function available in Perinci::Sub::Util when you want to create a modified subroutine based on another:

 package main;
 use Perinci::CmdLine;
 use Perinci::Sub::Util qw(gen_modified_sub);

 gen_modified_sub(
     output_name => 'f1',
     base_name   => 'Package::F1::f1',
     modify_args => {
         foo => sub { my $as = shift; delete $as->{cmdline_aliases}   },
         fee => sub { my $as = shift; $as->{cmdline_aliases} = {f=>{} },
     },
 );
 Perinci::CmdLine->new(url => '/main/f1')->run;

OUTPUT AND FORMATTING

I don't want borders!

By default, the text format produces bordered tables in interactive mode, e.g. if you have this program:

 $SPEC{foo} = {v=>1.1};
 sub foo {
     [200, "OK", [[1,2], [3,4], [5,6]]];
 }
 use Perinci::CmdLine::Any;
 Perinci::CmdLine::Any->new(url => '/main/foo')->run;

and you run it, you will have:

 % foo
 +---+---+
 | 1 | 2 |
 | 3 | 4 |
 | 5 | 6 |
 +---+---+

and if you use --format text-simple, only then it will become a tab-separated format:

 % foo --format text-simple
 1       2
 3       4
 5       6

But if you don't like this formatting and want to default to simpler formatting by default, you can add cmdline.default_format attribute to your function metadata:

 $SPEC{foo} = {v=>1.1, 'cmdline.default_format' => 'text-simple'};

Using this attribute, you can also default to JSON, and so on if you want.

You can also do this on a per-result basis, by adding cmdline.default_format attribute in your result metadata, e.g.:

 sub foo {
     [200, "OK", [[1,2], [3,4], [5,6]], {'cmdline.default_format'=>'text-simple'}];
 }

How to add support for new output format (e.g. XML, HTML)?

See Perinci::Result::Format.

My function has argument named 'format', but it is blocked by common option '--format'!

To add/remove/rename common options, see the documentation on common_opts attribute. In this case, you want:

 delete $cmd->common_opts->{format};
 #delete $cmd->common_opts->{format_options}; # you might also want this

or perhaps rename it:

 $cmd->common_opts->{output_format} = $cmd->common_opts->{format};
 delete $cmd->common_opts->{format};

How to stream output?

XXX

SHELL COMPLETION

How does completion work with P::C?

Currently bash, fish, tcsh, and zsh shells are supported.

Basically, you first activate completion for your script by invoking complete (or compctl in the case of zsh) shell internal command which tells the shell to invoke your script with special environment variable set so P::C can provide completion answer for the shell. For example, in bash the complete command is:

 complete -C yourscript yourscript

Then when you press Tab in this command:

 % yourscript --opt <Tab>

then bash will invoke yourscript with COMP_LINE environment set to yourscript --opt and COMP_POINT set to 17 (the cursor position). Perinci::CmdLine, looking that COMP_LINE is set, will try to provide a completion for the above command-line. First, it will retrieve the approriate function metadata, search the argument that corresponds to the command-line option --opt, and try several methods to complete its value (e.g. from argument specification property completion, if exists, or from in clause in the argument's schema, or if all things fail, usually from files/directories on the filesystem). It will then print the completion answer to STDOUT like this:

 one
 two
 three

Bash will then use this to provide completion for the user.

The exact complete/compctl command can be generated, for example, by Pod::Weaver::Section::Completion::PerinciCmdLine.

The downside of using the program to complete itself, like the abovementioned mechanism, is startup overhead. Everytime the Tab key is pressed, the shell will invoke the script. That's why it's important for P::C to keep having a small startup overhead. P::C::Lite is written specifically with this issue in mind. The goal of P::C::Lite is to have startup overhead of under 0.05s, so it can still feel instantaneous when doing tab completion. Of course, you also need to do your part by not loading too many modules or by delaying loading modules to avoid having a large startup overhead.

How to do custom completion for my argument?

By default, Perinci::Sub::Complete's complete_arg_val() can employ some heuristics to complete argument values, e.g. from the in clause or max and min:

 $SPEC{set_ticket_status} = {
     v => 1.1,
     args => {
         ticket_id => { ... },
         status => {
             schema => ['str*', in => [qw/new open stalled resolved rejected/],
         },
     },
 }

But if you want to supply custom completion, the Rinci::function specification allows specifying a completion property for your argument, for example:

 use Complete::Util qw(complete_array_elem);
 $SPEC{del_user} = {
     v => 1.1,
     args => {
         username => {
             schema => 'str*',
             req => 1,
             pos => 0,
             completion => sub {
                 my %args = @_;

                 # get list of users from database or whatever
                 my @users = ...;
                 complete_array_elem(array=>\@users, word=>$args{word});
             },
         },
         ...
     },
 };

You can use completion your command-line program:

 % del-user --username <tab>
 % del-user <tab> ; # since the 'username' argument has pos=0

My custom completion does not work, how do I debug it?

Completion works by the shell invoking our (the same) program with COMP_LINE and COMP_POINT environment variables. You can do something like this to see debugging information:

 % COMP_LINE='myprog --arg x' COMP_POINT=13 PERL5OPT=-MLog::Any::App TRACE=1 myprog --arg x

SEE ALSO

Perinci::CmdLine::Manual

HOMEPAGE

Please visit the project's homepage at https://metacpan.org/release/Perinci-CmdLine-Lite.

SOURCE

Source repository is at https://github.com/perlancar/perl-Perinci-CmdLine-Lite.

BUGS

Please report any bugs or feature requests on the bugtracker website https://rt.cpan.org/Public/Dist/Display.html?Name=Perinci-CmdLine-Lite

When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature.

AUTHOR

perlancar <perlancar@cpan.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2014 by perlancar@cpan.org.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.