How can I handle -r=<pattern> with Perl's Getopt::Long? - perl

I am parsing command line options in Perl using Getopt::Long. I am forced to use prefix - (one dash) for short commands (-s) and -- (double dash) for long commands (e.g., --input=file).
My problem is that there is one special option (-r=<pattern>) so it is long option for its requirement for argument, but it has to have one dash (-) prefix not double dash (--) like other long options. Is possible to setup Getopt::Long to accept these?

By default, Getopt::Long interchangeably accepts either single (-) or double dash (--). So, you can just use --r=foo. Do you get any error when you try that?
use strict;
use warnings;
use Getopt::Long;
my $input = 2;
my $s = 0;
my $r = 3;
GetOptions(
'input=s' => \$input,
's' => \$s,
'r=s' => \$r,
);
print "input=$input\n";
print "s=$s\n";
print "r=$r\n";
These sample command lines produce the same results:
my_program.pl --r=5
my_program.pl --r 5
my_program.pl -r=5
my_program.pl -r 5
input=2
s=0
r=5

Are you setting "bundling" on?
If so, you can disable bundling (but then, you won't be able to do things like use myprog -abc instead of myprog -a -b -c).
Otherwise, the only thing that comes to mind right now is to use Argument Callback (<>) and manually parse that option.

#!/usr/bin/perl
use strict; use warnings;
use Getopt::Long;
my $pattern;
GetOptions('r=s' => \$pattern);
print $pattern, "\n";
Output:
C:\Temp> zz -r=/test/
/test/
C:\Temp> zz -r /test/
/test/
Am I missing something?

Related

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.

How to escape ',' sign at command line?

When I do in perl script (source):
`perl -d:DebugHooks::DbInteract='s;e [\#DB::stack\,\#DB::goto_frames]' -e '1'`
The module gets two arguments:
s;e [\#DB::goto_frames\
\#DB::stack];
But I want to get only one:
s;e [\#DB::goto_frames,\#DB::stack];
How to escape ',' sign?
It's got to be your module splitting on the comma, not perl or the shell. Running the following under bash, I get only a single argument in #ARGV:
$ perl -w -E 'say join "\n", ("---", #ARGV, "---")' 's;e [\#DB::stack\,\#DB::goto_frames]'
---
s;e [\#DB::stack\,\#DB::goto_frames]
---
Edit:
I stand corrected. It is being split on commas by perl, presumably to allow passing multiple arguments to a module, as I proved to myself by creating a module at ./Devel/DbInteract.pm containing:
package Devel::DbInteract;
use strict;
use warnings;
use 5.010;
sub import {
say 'Received ' . scalar #_ . ' params:';
say join "\n", #_;
}
1;
and running the command:
$ PERL5LIB=. perl -d:DbInteract='s;e [\#DB::stack,\#DB::goto_frames]' -e ''
Received 3 params:
Devel::DbInteract
s;e [\#DB::stack
\#DB::goto_frames]
Judging by the source linked in the asker's answer, there does not appear to be any provision for escaping values or any other way to prevent this splitting. Your options, then, would be to either work around it by joining the params back together or submitting a patch to the perl source to add an allowance for escaping.
Perl does not care about escaping: https://github.com/Perl/perl5/blob/blead/perl.c#L3240-L3264
the -d flag just add next as zero line number into the script:
use Devel::DbInteract split(/,/,q{s;e [\#DB::stack\,\#DB::goto_frames]});
Some people advice me simple approach than patching perl. Just to use => in my case:
`perl -d:DebugHooks::DbInteract='s;e [\#DB::stack => \#DB::goto_frames]' -e '1'`

Adding a help command to a script

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.

Using Perl's Getopt::Long, how can I prevent the module from trying to match ambiguous option names?

I am using the Getopt::Long module to process command line arguments.
The typical behavior of this module is we could pass -f instead of full name of the variable --file. At the same time if I have another command line variable --find, and if I supply only -f at the command prompt, it would return with an error:
Option f is ambiguous (file, find).
I was wondering how can we curb such ambiguous usage?
Thanks in advance.
Have a look at the Getopt::Long documentation:
auto_abbrev
Allow option names to be abbreviated to uniqueness. Default is enabled
unless environment variable POSIXLY_CORRECT has been set, in which
case auto_abbrev is disabled.
Example:
use strict;
use warnings;
use Getopt::Long qw(:config no_auto_abbrev);
my ( $file, $fish );
GetOptions( "file=s" => \$file, "fish=s" => \$fish );
And the tests:
$ perl test.pl -fi 24
Unknown option: fi
$ perl test.pl -fis 24
Unknown option: fis
If you want to turn this auto abbreviation feature off you'll have to configure Getopt::Long using
use Getopt::Long qw(:config no_auto_abbrev) ;

Weird getopt =s handling?

code:
#!/usr/bin/env perl
use strict;
use warnings;
use locale;
my $prepinac_r = '';
my $array_name = '';
use Getopt::Long;
Getopt::Long::Configure ("bundling");
my $result = GetOptions(
"r=s" => \$prepinac_r,
"array-name=s" => \$array_name);
print STDERR "r: $prepinac_r\n";
print STDERR "array_name: $array_name\n";
running it:
script.pl --array-name=kokos -r=kure
output:
r: =kure
array_name: kokos
What I am doing wrong? What did I miss? Why -r gets "=kure" instead of "kure" ? Please help...
You're mixing short for and long form syntax. Short form syntax doesn't use = since that would make it less short.
"a|all" => \$opt_all,
"e|execute=s" => \$opt_execute,
Short form:
-aefoo
-a -efoo
-a -e foo
Long form:
--all --execute=foo
--all --execute foo
Here's an example of the short form you might be familiar with:
perl -le'print "Hello World";'
perl -l -e'print "Hello World";'
perl -l -e 'print "Hello World";'
Mr. Student, you should use double - for both switches >>
script.pl --array-name=coconut --r=chicken
...
r: chicken
array_name: coconut
I believe the behavior you are seeing is because of "bundling".
From Getopt::Long
Enabling this option will allow single-character options to be bundled. To distinguish bundles from long option names, long options must be introduced with -- and bundles with -
So if you are using "bundling", then -
--array-name=foo --r=bar # Works
-afoo -rbar # Also works
--array-name=foo -r=bar # Does not. as you've already seen
It also does not make sense to use bundling unless you are using options that do not require a parameter, and therefor can be "bundled"