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.
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
The test_prop.pl file currently accepts one argument:
my %options=();
getopts("i:", \%options);
$inputconf = $options{i};
This Perl script is getting called from a shell script as shown below:
perl test_prop.pl -i $FILE
I would like to pass one more argument to this perl script. How can I achieve that?
Following my template demonstrates how to handle multiple options, documenting the code for brief and full information.
#!/usr/bin/perl
#
# Description:
# Describe purpose of the program
#
# Parameters:
# Describe parameters purpose
#
# Date: Tue Nov 29 1:18:00 UTC 2019
#
# Author: Polar Bear
# https://stackoverflow.com/users/12313309/polar-bear
#
use strict;
use warnings;
use Getopt::Long qw(GetOptions);
use Pod::Usage;
my %opt;
my #args = (
'input|i=s',
'output|o=s',
'debug|d',
'help|?',
'man|m'
);
GetOptions( \%opt, #args ) or pod2usage(2);
print Dumper(\%opt) if $opt{debug};
pod2usage(1) if $opt{help};
pod2usage(-exitval => 0, -verbose => 2) if $opt{man};
pod2usage("$0: No files given.") if ((#ARGV == 0) && (-t STDIN));
__END__
=head1 NAME
program - brief on program's purpose
=head1 SYNOPSIS
program.pl [options] file(s)
Options:
-i,--input input filename
-o,--output output filename
-d,--debug output debug information
-?,--help brief help message
-m,--man full documentation
=head1 OPTIONS
=over 4
=item B<-i,--input>
Input filename
=item B<-o,--output>
Output filename
=item B<-d,--debug>
Print debug information.
=item B<-?,--help>
Print a brief help message and exits.
=item B<--man>
Prints the manual page and exits.
=back
=head1 DESCRIPTION
B<This program> accepts B<input> and processes to B<output> with purpose of achiving some goal.
=head1 EXIT STATUS
The section describes B<EXIT STATUS> codes of the program
=head1 ENVIRONMENT
The section describes B<ENVIRONMENT VARIABLES> utilized in the program
=head1 FILES
The section describes B<FILES> which used for program's configuration
=head1 EXAMPLES
The section demonstrates some B<EXAMPLES> of the code
=head1 REPORTING BUGS
The section provides information how to report bugs
=head1 AUTHOR
The section describing author and his contanct information
=head1 ACKNOWLEDGMENT
The section to give credits people in some way related to the code
=head1 SEE ALSO
The section describing related information - reference to other programs, blogs, website, ...
=head1 HISTORY
The section gives historical information related to the code of the program
=head1 COPYRIGHT
Copyright information related to the code
=cut
OP's post does not specify where getopts() is originated from
Let's assume getopts() originates from Getopts::Std
use strict;
use warnings;
use feature 'say';
use Getopt::Std;
use Data::Dumper;
$Getopt::Std::STANDARD_HELP_VERSION = 1;
my $version = '0.02';
my %opts;
getopts('iodn:',\%opts);
say 'INFO: -i input' if $opts{i};
say 'INFO: -o output' if $opts{o};
say "INFO: -n net $opts{n}" if $opts{n};
say Dumper(\%opts) if $opts{d};
my $fname = shift;
say "INFO: filename $fname" if $fname;
sub HELP_MESSAGE {
say "
This program performs specific task
USAGE:
program.pl [options] file(s)
-i input flag
-o output flag
-n dns network name
-d debug flag
";
}
sub VERSION_MESSAGE {
say "
program.pl Vesion $version 2020
";
}
Run the program as program.pl -i -o -0 something
Unknown option: 0
INFO: -i input
INFO: -o output
INFO: filename something
Run the program as program.pl -i -o -d something
INFO: -i input
INFO: -o output
$VAR1 = {
'd' => 1,
'o' => 1,
'i' => 1
};
INFO: filename something
Run the program as program.pl -i -o -d -n something
INFO: -i input
INFO: -o output
INFO: -n net something
$VAR1 = {
'd' => 1,
'i' => 1,
'n' => 'something',
'o' => 1
};
Run the program as program.pl --help
program.pl Vesion 0.02 2020
This program performs specific task
USAGE:
program.pl [options] file(s)
-i input flag
-o output flag
-n dns network name
-d debug flag
Run the program as program.pl --version
program.pl Vesion 0.02 2020
Documentation: Getopts::Std
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!)
I am trying to extract the Pod documentation from a Perl file. I do not want to convert the documentation to text as is done by Pod::Simple::Text. I just want the Pod text as Pod text, such that I can feed it into Pod::Template later. For example:
use warnings;
use strict;
use Pod::Simple::Text;
my $ps=Pod::Simple::Text->new();
my $str;
$ps->output_string( \$str );
$ps->parse_file($0);
print $str;
__END__
=head1 SYNOPSIS
prog [OPTIONS]
This will print the Pod as text. Is there a CPAN module that can give me the Pod text, that is:
=head1 SYNOPSIS
prog [OPTIONS]
instead?
Update
The solution should be able to handle Pod docs in strings, like
my $str = '__END__
=head1 SYNOPSIS';
Use the -u option for perldoc. This strips out the POD and displays it raw.
If you want to extract the POD from within a Perl program, you could do something like this:
my $rawpod;
if (open my $fh, '-|', 'perldoc', '-u', $filename) {
local $/;
my $output = <$fh>;
if (close $fh) {
$rawpod = $output;
}
}
If you really don't want to run perldoc as an executable, you might be interested that the perldoc executable is a very simple wrapper around Pod::Perldoc which you might want to consider using yourself.
This can be done using PPI:
use strict;
use warnings;
use PPI;
# Slurp source code
my $src = do { local ( #ARGV, $/ ) = $0; <> };
# Load a document
my $doc = PPI::Document->new( \$src );
# Find all the pod within the doc
my $pod = $doc->find('PPI::Token::Pod');
for (#$pod) {
print $_->content, "\n";
}
=comment
Hi Pod
=cut
1;
__END__
=head1 SYNOPSIS
prog [OPTIONS]
Outputs:
=comment
Hi Pod
=cut
=head1 SYNOPSIS
prog [OPTIONS]
Pod::Simple::SimpleTree will give it to you as a parse tree. You can convert that back to POD source easily enough.
Why not simply grepping the pods from the file:
open SR, $0;
print grep /^=/../^=cut$/, <SR>;
close SR;
I have:
my $man = 0;
my $help = 0;
## Parse options and print usage if there is a syntax error,
## or if usage was explicitly requested.
GetOptions('help|?' => \$help, man => \$man) or pod2usage(2);
pod2usage(1) if $help;
pod2usage(-verbose => 2) if $man;
#-----------------------------------------------------------------
#---------------- Documentation / Usage / Help ------------------
=head1 NAME
sample - Using GetOpt::Long and Pod::Usage
=head1 SYNOPSIS
sample [options] [file ...]
Options:
-help brief help message
-man full documentation
=head1 OPTIONS
=over 8
=item B<-help>
Print a brief help message and exits.
=item B<-man>
Prints the manual page and exits.
=back
=head1 DESCRIPTION
B<This program> will read the given input file(s) and do something
useful with the contents thereof.
=cut
Pretty much copy / pasted from the online example. However, when I do script.pl --help nothing prints, and the script exits.
As has been stated, the spacing of pod documentation matters. Additionally, it's not necessary to duplicate your options in the synopsis, instead just leave them in the options section.
The following is a cleaned up version of your trial usage of Pod::Usage
use strict;
use warnings;
use Getopt::Long qw(GetOptions);
use Pod::Usage qw(pod2usage);
## Parse options and print usage if there is a syntax error,
## or if usage was explicitly requested.
GetOptions(
'help|?' => \my $help,
'man' => \my $man,
) or pod2usage(-verbose => 0);
pod2usage(-verbose => 1) if $help;
pod2usage(-verbose => 2) if $man;
## Check for File
pod2usage("$0: No filename specified.\n") unless #ARGV;
#-----------------------------------------------------------------
#---------------- Documentation / Usage / Help ------------------
=head1 NAME
sample - Using GetOpt::Long and Pod::Usage
=head1 SYNOPSIS
sample [file]
=head1 OPTIONS
=over 8
=item B<--help>
Print a brief help message and exits.
=item B<--man>
Prints the manual page and exits.
=back
=head1 DESCRIPTION
B<This program> will read the given input file(s) and do something
useful with the contents thereof.
=cut
Miller has the answer.
When you create POD documentation, you need a blank line between each paragraph including the command paragraphs. The POD Documentation does not make that clear. Also, all POD command paragraphs must start in the first column. For example, this is wrong:
=head1 NAME
foo.cmd
=head1 DESCRIPTION
This is my description of my command.
....
I need to space this out:
=head1 NAME
foo.cmd
=head1 DESCRIPTION
This is my description.
Otherwise, POD would interpret it this way:
=head1 NAME foo.cmd =head1 DESCRIPTION This is my description of my command.
Now, you don't have a header named NAME or DESCRIPTION, so your pod2usage won't print.
I also need to start my commands on column 1. This won't work:
=over 4
=item *
blah, blah, blah...
I need to do this:
=over 4
=item *
blah, blah, blah
=back
You can run the podchecker program to check your pod and make sure everything is correct. This should be in the same directory as the perl command. I put your pod documentation into test.pod and ran this command:
$ podchecker test.pod
*** WARNING: empty section in previous paragraph at line 4 in file test.pod
*** WARNING: empty section in previous paragraph at line 10 in file test.pod
*** ERROR: Apparent command =cut not preceded by blank line at line 21 in file test.pod
*** WARNING: empty section in previous paragraph at line 18 in file test.pod
test.pod has 1 pod syntax error.
The empty section in previous paragraph warnings come from not skipping a line after =head1.
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.