I want to handle a feature which seems to me almost natural with programs, and I don't know how to handle it with Getopt perl package (no matter Std ot Long).
I would like something like:
./perlscript <main option> [some options like -h or --output-file some_name]
Options will be handled with - or --, but I want to be able to let the user give me the main and needed option without dashes.
Is Getopt able to do that, or do I have to handle it by hand?
It sounds as though you are talking about non-options -- basic command-line arguments. They can be accessed with #ARGV. The Getopt modules will pass regular arguments through to your script unmolested:
use strict;
use warnings;
use Getopt::Long;
GetOptions (
'foo' => \my $foo,
'bar=s' => \my $bar,
);
my #main_args = #ARGV;
# For example: perl script.pl --foo --bar XXX 1 2 3
# Produces: foo=1 bar=XXX main_args=1 2 3
print "foo=$foo bar=$bar main_args=#main_args\n";
If you want to have it written without a -, and it's also not optional (as you specifiy), then by any reasoning it isn't an option at all, but an argument. You should simply read yourself via
my $mainarg = shift
and then let Getopt do its thing. (You might want to check $#ARGV afterwards to verify that the main argument was actually given.)
Related
I am new to Perl and I'm confused with its handling of optional arguments.
If I have a perl script that's invoked with something along the lines of:
plgrep [-f] < perl regular expression > < file/directory list >
How would I determine whether or not the -f operator is given or not on the command line?
All of the parameters passed to your program appear in the array #ARGV, so you can simply check whether any of the array elements contain the string -f
But if you are writing a program that uses many different options in combination, you may find it simpler to use the Getopt::Long module, which allows you to specify which parameters are optional, which take values, whether there are multiple synonynms for an option etc.
A call to GetOptions allows you to specify the parameters that your program expects, and will remove from #ARGV any that appear in the command line, saving indicators in simple Perl variables that reflect which were provided and what values, if any, they had
For instance, in the simple case that you describe, you could write your code like this
use strict;
use warnings 'all';
use feature 'say';
use Getopt::Long;
use Data::Dump;
say "\nBefore GetOptions";
dd \#ARGV;
GetOptions( f => \my $f_option);
say "\nAfter GetOptions";
dd $f_option;
dd \#ARGV;
output
Before GetOptions
["-f", "regexp", "file"]
After GetOptions
1
["regexp", "file"]
So you can see that before the call to GetOptions, #ARGV contains all of the data in the command line. But afterwards, the -f has been removed and variable $f_option is set to 1 to indicate that the option was specified
Use Getopt::Long. You could, of course, parse #ARGV by hand (which contains command line arguments), but there is no reason to do that with the existence of good modules for the job.
use warnings;
use strict;
use Getopt::Long;
# Set up defaults here if you wish
my ($flag, $integer, $float, $string);
usage(), exit if not GetOptions(
'f|flag!' => \$flag,
'integer:i' => \$integer,
'float:f' => \$float,
'string:s' => \$string
);
# The script now goes. Has the flag been supplied?
if (defined($flag)) { print "Got flag: $flag\n" } # it's 1
else {
# $flag variable is 'undef'
}
sub usage {
print "Usage: $0 [options]\n"; # -f or -flag, etc
}
The $flag can simply be tested for truth as well, if that is sufficient. To only check whether -f is there or not, need just: GetOptions('f' => \$flag); if ($flag) { };.
The module checks whether the invocation specifies arguments as they are expected. These need not be entered, they are "options." However, for an unexpected invocation a die or warn message is printed (and in the above code our usage message is also printed and the script exits). So for script.pl -a the script exits with messages (from module and sub).
Abbreviations of option names are OK, if unambiguous; script.pl -fl 0.5 exits with messages (-flag or -float?) while script.pl -i 5 is OK and $integer is set to 5. On the other hand, if an integer is not supplied after -i that is an error, since that option is defined to take one. Multiple names for options can be specified, like f|flag. Etc. There is far more.
So basically what I mean is: is there a way for me to disable the function print's output when invoking a perl script at the command line? If not available in the base implementation of perl then maybe CPAN?
Things that I am aware of:
perl has a debugger
redirecting stdout to some file and then wiping it (useless in my case since I need STDOUT for something else)
having all lines that start with a print commented (doesn't scale, is ugly etc...)
redirecting in UNIX to dev/null
I am not referring to any warnings, exceptions, errors etc... Just the standard print function or any of it's very close siblings.
Similar to how you'd use C's #ifdef but at file scope and not having to write so much for so little.
I'm willing to install a cpan module in case it offers this kind of functionality.
I am limited to Perl 5.14.
Assuming you are writing this script yourself, or can edit it, something like below will work.
use Getopt::Long;
our $VERBOSE = 0;
GetOptions ('verbose+' => \$VERBOSE);
# ...
xprint(1, "Yadda yadda yadda");
# ...
sub xprint {
my ($pri, $msg) = #_;
print("$msg\n") if $pri >= $VERBOSE;
}
EDIT:
Or without priority levels:
use Getopt::Long;
our $VERBOSE = '';
GetOptions ('verbose' => \$VERBOSE);
# ...
xprint("Yadda yadda yadda");
# ...
sub xprint {
# can also be replaced with printf(#_) for more cowbell.
print("$_[0]\n") if $VERBOSE;
}
EDIT:
As an aside, for #ifdef functionality, replace the whole getopt section with a simple constant:
use constant VERBOSE => 1;
And remove the $ from VERBOSE after the print statement:
print("$_[0]\n") if VERBOSE;
I have two file tmp.pl and tmp2.pl. I want to call tmp2.pl with a require command but also send a parameter. Is there a better way of doing this?
tmp.pl
require "tmp2.pl" "passed parameter";
tmp2.pl
print #_;
As far as I know, require cannot be used to send a parameter. But that's a good thing, I think, because I cannot think of a reason why you should want to. Looks to me that your design is wrong.
tmp2.pl should be either:
a independent perl program, which you should run with system or qx()
a module, with optional exported tags etc.
a package which defines a class
but that's just my idea....
There's probably a better way to accomplish whatever it is you're trying to do, but you could achieve your current sub goal with something like
{
local #_ = ("passed parameter");
require "tmp2.pl";
}
I might consider this idiom in a place where I wanted to run a perl script from within a perl script. That is, I could say
{
local #ARGV = ("foo","bar");
require "my_script.pl";
}
instead of
system("perl","my_script.pl","foo","bar");
(There are plenty of subtle and not-so-subtle differences between these two calls, so a lot depends on what "features" of these calls you need)
Yes, I think this should work. Use "require" to include the script. After that you can pass the parameter by calling the sub function. The modified script can be
require "tmp2.pl" ;
subfunc(parameter);
print #_;
Supposing that you want to execute other program and capture its output I'm partial to using the IPC::Run module.
#!/usr/bin/perl
use strict;
use warnings;
use IPC::Run qw( run );
run( [ './tmp2.pl', #ARGV ], \'', \my $out, \my $err );
print "out: $out\n";
print "err: $err\n";
If I have a command line like:
my_script.pl -foo -WHATEVER
My script knows about --foo, and I want Getopt to set variable $opt_foo, but I don't know anything about -WHATEVER. How can I tell Getopt to parse out the options that I've told it about, and then get the rest of the arguments in a string variable or a list?
An example:
use strict;
use warnings;
use Getopt::Long;
my $foo;
GetOptions('foo' => \$foo);
print 'remaining options: ', #ARGV;
Then, issuing
perl getopttest.pl -foo -WHATEVER
gives
Unknown option: whatever
remaining options:
You need to configure "pass_through" option via Getopt::Long::Configure("pass_through");
Then it support actual options (e.g. stuff starting with "-" and without the special "--" delimiter to signify the end of "real" options).
Here's perldoc quote:
pass_through (default: disabled)
Options that are unknown, ambiguous or supplied with an invalid option value are passed through in #ARGV instead of being flagged as errors. This makes it possible to write wrapper scripts that process only part of the user supplied command line arguments, and pass the remaining options to some other program.
Here's an example
$ cat my_script.pl
#!/usr/local/bin/perl5.8 -w
use Getopt::Long;
Getopt::Long::Configure("pass_through");
use Data::Dumper;
my %args;
GetOptions(\%args, "foo") or die "GetOption returned 0\n";
print Data::Dumper->Dump([\#ARGV],["ARGV"]);
$ ./my_script.pl -foo -WHATEVER
$ARGV = [
'-WHATEVER'
];
Aren't the remaining (unparsed) values simply left behind in #ARGV? If your extra content starts with dashes, you will need to indicate the end of the options list with a --:
#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Long;
use Data::Dumper;
my $foo;
my $result = GetOptions ("foo" => \$foo);
print Dumper([ $foo, \#ARGV ]);
Then calling:
my_script.pl --foo -- --WHATEVER
gives:
$VAR1 = [
1,
[
'--WHATEVER'
]
];
PS. In MooseX::Getopt, the "remaining" options from the command line are put into the extra_argv attribute as an arrayref -- so I'd recommend converting!
I think the answer here, sadly though, is "no, there isn't a way to do it exactly like you ask, using Getopt::Long, without parsing #ARGV on your own." Ether has a decent workaround, though. It's a feature as far as most people are concerned that any option-like argument is captured as an error. Normally, you can do
GetOptions('foo' => \$foo)
or die "Whups, got options we don't recognize!";
to capture/prevent odd options from being passed, and then you can correct the user on usage. Alternatively, you can simply pass through and ignore them.
I have an existing Perl program that uses Getopt package and Getopt::Long::Configure with permute as one of the options. However, now I need to keep the order of the options entered by the user. There is an option $RETURN_IN_ORDER mentioned in the Long.pm, however doesn't seem to be used anywhere at all.
When I pass return_in_order, I am getting the following error.
Getopt::Long: unknown config parameter "return_in_order" at C:/Program Files/IBM/RationalSDLC/common/lib/perl5/5.8.6/Getopt/Long.pm line 1199.
Can someone please tell me if this is supported at all and if so, the right way to use? If not, I would like to know the other alternatives I have.
Thanks.
I think you want "require_order"
http://perldoc.perl.org/Getopt/Long.html#Configuring-Getopt%3a%3aLong
You have two answers pointing at 'require_order', but I think both those answers are misunderstanding both what 'require_order' does and what you seek.
If 'require_order' is unset, then you can write (on the command line):
-a file -b
Where both -a and -b are simple options (not taking an argument). With 'require_order' set, the presence of 'file' terminates the options and the '-b' flag becomes a 'file name'.
What I think you are seeking is a mechanism that allows you to tell that '-a' appeared before both 'file' and '-b'. I do not think that Getopt::Long supports that. In fact, I'm not aware of any Getopt::* module that does that (other than my own, unpublished, Getopt::JLSS which I sometimes use). [If you are interested in the code, send me an email at Gmail, using a dot between first and last name.]
It's called require_order, according to the manpage I have here. :-)
Maybe it would have helped if you provided an example call to your program.
Getopt::Long supports mixing options with ordinary "positional" args:
./foo --option1 --option2 file1 file2 file3
If you only parse the options with GetOptions, file1 to 3 will be left in #ARGV in the order they appeared. Maybe you can use that behaviour to achieve what you want.
Furthermore, you can specify the same option multiple times and have the results put in an array (in order!):
#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Long;
my #permute;
GetOptions(
'p=s' => \#permute,
);
print "$_\n" for #permute;
"perl foo.pl -p=1 -p=2 -p=3 -p=4" results in this output: (Note: "-p X" works the same as "-p=X")
1
2
3
4
The requirement in my case is very different as the command line parameters are going to be a combination of options and arguments, such as the following.
mytool -arg1 value 1 -arg2 value2 -arg3 value3
And this is why require_order is not particularly helpful.
And as I just figured out, the problem is not really with the parsing component. It's with the hash object that is used to store them.
As tsee right pointed out, the order will be same if we use the default configurations.
Thanks folks, anyways.
Needing to know the order in which options were specified is why I still use Getopt::Mixed. That module has a nextOption function which you can loop over, and it will give you the options in the order they were on the command line.
My use case for this is include and exclude regexps:
myscript /usr --include /usr/local/bin --exclude /usr/local
What actually happens of course also depends on how I handle the includes and excludes inside by script, but I need to know the order.
Very late to the party. I did this using an option-handling subroutine. My particular use case is that I need to handle any number of -e <foo> and -f <foo> options in any order, preserving that order (for a personal project).
use Data::Dumper; use Getopt::Long; use strict; use warnings;
my #Sources; # Each element is [was -f, parameter]
my $dr_save_source = sub { # Called each time we see -e or -f
my ($which, $text) = #_;
push #Sources, [$which eq "f", $text];
}; # dr_save_source
GetOptions("e=s#" => $dr_save_source, "f=s#" => $dr_save_source);
print Dumper(\#Sources);
Providing the subroutine reference $dr_save_source as the value associated with an option specification (=> $dr_save_source) tells GetOptions to call that subroutine to handle the option whenever it shows up.
This might get a bit clumsy as the number of options increased, but it is ... an option.