Adding a help command to a script - perl

Is there a standard way of adding a help function to a script? The simplest way would maybe to take an argument and print some text if it's "-help" or something. Does anyone have any examples on how to do this?
Thanks!

Consider Getopt::Long plus Pod::Usage. My usual pattern for writing CLI tools:
#!/usr/bin/env perl
# ABSTRACT: Short tool description
# PODNAME: toolname
use autodie;
use strict;
use utf8;
use warnings qw(all);
use Getopt::Long;
use Pod::Usage;
# VERSION
=head1 SYNOPSIS
toolname [options] files
=head1 DESCRIPTION
...
=cut
GetOptions(
q(help) => \my $help,
q(verbose) => \my $verbose,
) or pod2usage(q(-verbose) => 1);
pod2usage(q(-verbose) => 1) if $help;
# Actual code below

easy to use this :
if( $ARGV[0] eq '-h' || $ARGV[0] eq '-help')
{
help();
exit;
}
sub help { print "My help blah blah blah\n";
}

Take a look at https://github.com/qazwart/SVN-Watcher-Hook/blob/master/svn-watch.pl. I use a technique to combine the Getopt::Long module and the Pod::Usage module.
The main action occurs in lines 97 through 106 and in lines 108 through 110.
The Getopt::Long is a very common module to use since it handles command line arguments with easy. Using Pod documentation is rarer. However, all CPAN modules and all Perl built in modules use Pod documentation, so if you don't know it, learn it. POD is not very difficult to learn, and it's built into Perl, so all Perl programs can be self-documenting. You can print out the POD documentation of any program by using the perldoc command. Try this:
$ perldoc File::Find
You can also use the pod2html, pod2text and other types of translation commands to print POD documentation into HTML, etc.
Before I knew about POD, I would put something like this at the top of my program:
########################################################
# USAGE
#
my $USAGE =<<USAGE;
Usage:
foo [ -baz -fu <bar>] [-help]
where:
baz: yadda, yadda, yadda
fu: yadda, yadda, yadda
help: Prints out this helpful message
USAGE
#
######################################################
Then, in my program, I could do this:
if ($help) {
print "$USAGE\n";
exit 0;
}
This way, someone could look at the code and read the usage text. This would also be the same text that would print out when you used the -help parameter.

The way I do this is to utilise Getopt::Std to find an -h flag from the command line arguments.
use strict;
use warnings;
use Getopt::Std;
my %args;
getopts('h', \%args);
my $help = "help goes here. You can use
more than one line to format the text";
die $help if $args{h};
# otherwise continue with script...
A more sophisticated approach is to use POD::usage, although I have not tried this way personally.

Related

How to call a perl script from another perl script and transfer parameter?

I'm trying to call a perl script from another perl script, and transfer the parameter to it.
For example:
there is a script: main.pl
I use command line to run this script, and give a value to parameter "$directory", then I call another perl script "sub.pl". I want to pass the value of "$directory" to the parameter "$path" from sub.pl.
(in brief, sub.pl has the parameter $path, main.pl has the parameter $directory, I want to call sub.pl in main.pl, and pass the $directory value to $path)
Sorry for my verbose description...Anyway, which function can do this job? Thanks.
You did not provide any sample of code you have tried -- how can we know what is your vision of the code?
Ok, bellow I provide a sample of main script dir_main.pl and secondary script dir_sub.pl just for demonstration purpose how I would do it.
Both scripts can be run with parameters '--help' (-h) or '--man' (-m) to obtain help and man page describing usage and full documentation for the scripts. Script dir_sub.pl has extra option '--debug' (-d) to print content of options hash.
USAGE: perl dir_main.pl --dir c:\Users -- Windows
USAGE: dir_main.pl --dir /usr/home -- Linux
NOTE:
in Linux both scripts should be made executable with following command chmod og+x dir_main.pl dir_sub.pl before they can run from shell without specifying perl
(shell knows from shebang that the scripts should be run with perl interpreter)
Source code of: dir_main.pl
#!/usr/bin/perl
#
# DESCRIPTION:
# Sample code 'dir_main.pl' written for StackOverflow
#
# DATE:
# Jan 10, 2020
#
# AUTHOR:
# Polar Bear <https://stackoverflow.com/users/12313309/polar-bear>
#
use strict;
use warnings;
use Getopt::Long qw(GetOptions);
use Pod::Usage;
my %opt;
GetOptions(
'dir|d=s' => \$opt{dir},
'help|h' => \$opt{help},
'man|m' => \$opt{man}
) or pod2usage(2);
pod2usage(1) if $opt{help};
pod2usage(-exitval => 0, -verbose => 2) if $opt{man};
system('perl','.\dir_sub.pl','--path',$opt{dir}) if $opt{dir};
exit 0;
=pod
=head1 NAME
program.pl - short description of the program
=head1 SYNOPSIS
program.pl [options]
Options:
--dir,-d input directory
--help,-h brief help message
--man,-m full documentation
=head1 OPTIONS
=over 4
=item B<--dir|-d>
Input directory
=item B<--help|-h>
Print a brief help message and exit
=item B<--man|-m>
Prints the manual page and exit
=back
=head1 DESCRIPTION
B<This program> surve some purpose to produce pre-defined result
=head1 AUTHOR
Polar Bear Jan 10, 2020
=head1 REPORTING BUGS
E-mail L<mailto:bugs#inter.net>
=head1 COPYRIGHT
Copyright information
=head1 SEE ALSO
L<The Perl Home page|http://www.perl.org/>
=cut
Source code of: dir_sub.pl
#!/usr/bin/perl
#
# DESCRIPTION:
# Sample code 'dir_sub.pl' written for StackOverflow
#
# DATE:
# Jan 10, 2020
#
# AUTHOR:
# Polar Bear <https://stackoverflow.com/users/12313309/polar-bear>
#
use strict;
use warnings;
use feature 'say';
use Getopt::Long qw(GetOptions);
use Pod::Usage;
use Data::Dumper;
my %opt;
GetOptions(
'path|p=s' => \$opt{path},
'help|h' => \$opt{help},
'man|m' => \$opt{man},
'debug|d' => \$opt{debug}
) or pod2usage(2);
pod2usage(1) if $opt{help};
pod2usage(-exitval => 0, -verbose => 2) if $opt{man};
print Dumper(\%opt) if $opt{debug};
list($opt{path}) if $opt{path};
sub list {
my $path = shift;
opendir my $dh, $path
or die "ERROR: opendir couldn't open $path";
map{ say $_ } readdir($dh);
close $dh;
}
exit 0;
=pod
=head1 NAME
program.pl - short description of the program
=head1 SYNOPSIS
program.pl [options]
Options:
--path,-p input path to list
--help,-h brief help message
--man,-m full documentation
--debug,-d debug information
=head1 OPTIONS
=over 4
=item B<--path|-p>
Input path to list files
=item B<--help|-h>
Print a brief help message and exit
=item B<--man|-m>
Prints the manual page and exit
=item B<--debug|-d>
Prints the debug information
=back
=head1 DESCRIPTION
B<This program> surve some purpose to produce pre-defined result
=head1 AUTHOR
Polar Bear Jan 10, 2020
=head1 REPORTING BUGS
E-mail L<mailto:bugs#inter.net>
=head1 COPYRIGHT
Copyright information
=head1 SEE ALSO
L<The Perl Home page|http://www.perl.org/>
=cut
Very first question: does sub.pl have a mechanism for setting $path from the command-line?
If not, you're not going to do it: you can't arbitrarily set an internal variable to some value... that would open up all sorts of security risks & dangers to any piece of code!
Assuming you can call something like sub.pl path=/my/path then it's easy
(but I have horrible suspicion you mean the former!)

How to pass command line arguments along with perl modules using perl?

I treid one of my previous example to pass input and output files via command line arguments using perl?
Previous example:
[Is it possible to add the file out in the File::Find::Rule using perl?
code which i tried to add input and output file as command line arguments:(generate.pl).I got struck how to include command line arguments to read and copy the files along with perl modules.
use strict;
use warnings 'all';
use CGI qw(:all);
#generate the user file arguments
use Getopt::Long 'GetOptions';
use File::Path qw( make_path );
use File::Copy qw( copy move);
use File::Find::Rule qw( );
GetOptions(
'prjroot=s' => \my $input_dir,
'outdir=s' => \my $output_dir,
);
my %created;
for my $in (
File::Find::Rule
->file()
->name(qr/^[^.].*\.yfg$/)
->in($input_dir)
) {
my $match_file = substr($in, length($input_dir) + 1);
my ($match_dir) = $match_file =~ m{^(.*)/} ? $1 : '.';
my $out_dir = $output_dir . '/' . $match_dir;
my $out = $output_dir . '/' . $match_file;
make_path($out_dir) if !$created{$out_dir}++;
copy($in, $out);
}
Error occured:
./generate.pl: line 1: use: command not found
./generate.pl: line 2: use: command not found
./generate.pl: line 3: syntax error near unexpected token `('
./generate.pl: line 3: `use CGI qw(:all);'
perl execution should be as follows:
I should copy the contents from one directory to another directory along with perl modulES(I.E File::Find::Rule)
./generate.pl -prjroot "/home/bharat/DATA" -outdir "/home/bharat/DATA1"
Help me fix my issues .
You miss the perl comment in the first line:
#!<path_to_perl>/perl
use strict;
use warnings 'all';
use CGI qw(:all);
#generate the user file arguments
.....
Your program will also work if you call it with the perl Interpreter in front of your command:
perl ./generate.pl -prjroot "/home/bharat/DATA" -outdir "/home/bharat/DATA1"
You've already seen what the problem is. But I'll just add that perldoc perldiag gives pretty good explanations for any error message you get from Perl. In this case, searching for "command not found" would give you this:
%s: Command not found
(A) You've accidentally run your script through csh or another shell instead of Perl. Check the #! line, or manually feed your script into Perl yourself. The #! line at the top of your file could look like
#!/usr/bin/perl -w
And that's particularly impressive, given that this error isn't actually generated by Perl - but by your shell.

Perl Getopt modules

I am new to Perl. I am trying to understand the Getopt::Long module and I have read materials online as well as books. Nothing is 100% clear to me hence this question.
I will be calling the command line in the following formats
script -m file
or
script -v hostname -m file
How can I accomplish this with Getopt::Std or Getopt::Long?
You can use either.
However, I'd advise you use Getopt::Long and Pod::Usage
#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Long qw(GetOptions);
use Pod::Usage qw(pod2usage);
GetOptions(
'm=s' => \my $filename,
'v=s' => \my $hostname,
'help|?' => \my $help,
'man' => \my $man,
) or pod2usage(-verbose => 0);
pod2usage(-verbose => 1) if $help;
pod2usage(-verbose => 2) if $man;
# Fake Parameter validation
pod2usage("$0: No filename specified.\n") unless $filename;
print "Hostname = '$hostname'\n";
print "Filename = '$filename'\n";
1;
__END__
=head1 NAME
yourscript.pl - does something
=head1 SYNOPSIS
./yourscript.pl
=head1 OPTIONS
=over 4
=item --m
Filename, for some reason.
=item --v
Hostname, maybe make this -h so it's not confused with version flags?
=item --help
Print this summary.
=item --man
Print the complete manpage
=back
=head1 DESCRIPTION
This does stuff.... and other stuff
And this is all there is to say about it
=head1 AUTHOR
Written by You
I would advise against your flag choices though. It's often sufficient to just pass the filename as a loose parameter to a script instead of requiring a flag. Then just pull it from #ARGV after calling your GetOptions. As for -v for hostname, that's typically used to check a version of a script. Instead I'd use --hostname or -h.

How can one respond to the --help flag with Getopt::Std?

I want my script to print a help message when it is run with the --help command line option. Based on the Getopt::Std documentation, this sub should do the trick:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.014;
use Getopt::Std;
sub HELP_MESSAGE {
say "HELP MESSAGE";
}
But it prints nothing. I also tried adding this, out of curiosity:
for (#ARGV) {
HELP_MESSAGE() if /--help/;
}
It actually works, but seems rather sloppy. I know using the -h flag would be quite simple, but I would like to have both.
The documentation of Getopt::Std says
If - is not a recognized switch letter, getopts() supports arguments --help and --version. If main::HELP_MESSAGE() and/or main::VERSION_MESSAGE() are defined, they are called; ...
So try this:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.014;
use Getopt::Std;
$Getopt::Std::STANDARD_HELP_VERSION = 1;
our $VERSION = 0.1;
getopts(''); # <<< You forgot this line, and `getopt()` DOES NOT work
sub HELP_MESSAGE {
say "HELP MESSAGE";
}
Test run:
$ ./t00.pl --help
./t00.pl version 0.1 calling Getopt::Std::getopts (version 1.07),
running under Perl version 5.16.3.
HELP MESSAGE

How do you create application-level options using Perl's App::Cmd?

Update from FMc
I'm putting a bounty on this question, because I'm puzzling over the same problem. To rephrase the question, how do you implement application-level options (those that apply to an entire program, script.pl), as opposed to those that apply to individual commands (search in this example).
The original question
How can I use App::Cmd to create an interface like this
script.pl --config <file> search --options args
?
I can do:
./script.pl search --options args
./script.pl search args
./script.pl search --options
What I'm trying to achieve is getting an option for the config file like so:
./script.pl --config file.conf search --options args
I've looked at App::Cmd::Tutorial on cpan but so far I haven't had any luck getting it to work.
You can specify global options in App::Cmd like below. We need three files:
script.pl:
use Tool;
Tool->run;
Tool.pm:
package Tool;
use strict; use warnings;
use base 'App::Cmd';
sub global_opt_spec {
['config=s' => "Specify configuration file"];
}
1;
and Tool/Command/search.pm:
package Tool::Command::search;
use strict; use warnings;
use base 'App::Cmd::Command';
sub opt_spec {
["option" => "switch on something"],
}
sub execute {
my ($self, $opt, $args) = #_;
warn "Running some action\n";
warn 'Config file = ' . $self->app->global_options->{config}, "\n";
warn 'Option = ' . $opt->{option}, "\n";
}
1;
The example shows how to define global option and access it from within search action.