perl parse command line options - perl

I am trying to get parameters from command line and parse it and if the parameters are right call certain functions based on it.I am new to perl, can some one let know how to achieve this
script.pl aviator #switch is valid and should call subroutine aviator()
script.pl aviator debug #valid switch and should call subroutine aviator_debug
script.pl admin debug or script.pl debug admin #valid switch and should call subroutine admin_debug()
script.pl admin #valid switch and should call subroutine admin()
script.pl dfsdsd ##invalid switch ,wrong option

Since you are dealing with plain words (and not --switches), just look at #ARGV, which is an array of the command line options. Applying a simple if/elsif/etc to that data should serve your needs.
(For more complex requirements, I'd suggest the Getopt::Long::Descriptive module.)

Having lots of checks against specific strings is a recipe for a maintenance nightmare as your system grows more and more complex. I strongly recommend implementing some kind of dispatch table.
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
my %commands = (
aviator => \&aviator,
aviator_debug => \&aviator_debug,
admin => \&admin,
admin_debug => \&admin_debug,
debug_admin => \&admin_debug,
);
my $command = join '_', #ARGV;
if (exists $commands{$command}) {
$commands{$command}->();
} else {
die "Illegal options: #ARGV\n";
}
sub aviator {
say 'aviator';
}
sub aviator_debug {
say 'aviator_debug';
}
sub admin {
say 'admin';
}
sub admin_debug {
say 'admin debug';
}

variant 1:
#!/usr/bin/perl
my $command=join(' ',#ARGV);
if ($command eq 'aviator') { &aviator; }
elsif ($command eq 'aviator debug' or $command eq 'debug aviator') { &aviator_debug; }
elsif ($command eq 'admin debug' or $command eq 'debug admin') { &admin_debug; }
elsif ($command eq 'admin') { &admin; }
else {print "invalid option ".$command."\n";exit;}
variant 2:
#!/usr/bin/perl
if (grep /^aviator$/, #ARGV ) {
if (grep /^debug$/, #ARGV) { &aviator_debug; }
else { &aviator; }
} elsif (grep /^admin$/, #ARGV ) {
if (grep /^debug$/, #ARGV) { &admin_debug; }
else { &admin; }
} else { print "invalid option ".join(' ',#ARGV)."\n";exit;}
exit;
variant 3:
#!/usr/bin/perl
use Switch;
switch (join ' ',#ARGV) {
case 'admin' { &admin();}
case 'admin debug' { &admin_debug; }
case 'debug admin' { &admin_debug; }
case 'aviator' { &aviator; }
case 'aviator debug' { &aviator_debug; }
case 'debug aviator' { &aviator_debug; }
case /.*/ { print "invalid option ".join(' ',#ARGV)."\n";exit; }
}

Here is my take on the problem
#!/usr/bin/perl
use 5.14.0;
my $arg1 = shift;
my $arg2 = shift;
given ($arg1) {
when ($arg1 eq 'aviator') {say "aviator"}
when ($arg1 eq 'admin' && !$arg2) {say "admin"}
when ($arg1 =~ /^admin|debug$/ && $arg2 =~ /^admin|debug$/) {say "admin debug"}
default {say "error";}
}

Related

Perl error handling

how can i cache errors in perl? Is there try/cache like in JS? I would like if any error occurs to go to the start of the script.
And if anyone has an idea of improvement for the script below let me know because this is my first one in perl. The script just has to loop forever and never stop. :)
#!/usr/bin/perl
use strict;
use warnings;
use LWP::UserAgent;
use JSON;
use HTTP::Request::Common qw(POST GET);
use Encode qw(encode);
use DBI;
use Time::Piece;
# Beware: we disable the SSL certificate check for this script.
$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME}=0;
# Debugging: off=0, medium=3, extensive=5
my $debuglevel=0;
my ($host,$username,$password)=('192.168.xxx.xxx','xxxx','xxxx');
# Define cms api key and nodeid.
my ($cmsapi,$cmsnode)=('xxxxxxxxx','1');
# Define all parameters to be logged each script's iteration.
# #parameterlist[x][$parameterid,$parameterlongtext,$parametershorttext,$data]
# which corresponds for FHEM's DbLog with:
# #parameterlist[x][$parameterid,$parameterlongtext,READING ,VALUE]
# $parameterlist[x][3] will be populated by the script, thus here undefined in each line (the last value is missing).
my #parameterlist=(
[3922,"Status TC","statusHeatPump"],
[3931,"Zunanja temperatura","outsideTemperature"],
[3924,"Status zalogovnika","statusBuffer"],
[3925,"Status bojlerja","statusBoiler"],
[3940,"Temperatura bojlerja","boilerTemperature"],
[3943,"Temperatura zalogovnika","bufferTemperature"],
[4331,"Temperatura nadstropja","floorTemperature"],
[3811,"Temperatura pritličja","groundTemperature"],
);
# We substitute the text for the burner's status with an integer, so plots are easier.
# Define which parameter holds the burner's status.
my $parameterstatusHeatPump=3922;
my #statusHeatPumpmatrix=(
["Off",0],
["Heating mode",50],
);
sub trim() {
my $str = $_[0];
$str =~ s/^\s+|\s+$//g;
return $str;
};
print "DEBUG: *** Script starting ***\n" if($debuglevel>0);
while (1) {
sleep 1;
my $ua=LWP::UserAgent->new;
my $request=HTTP::Request->new(GET=>'https://'.$host.'/api/auth/login.json?user='.$username.'&pwd='.$password);
my $response=$ua->request($request);
my $decoded=decode_json($response->decoded_content( charset => 'none'));
my $success=$decoded->{'Result'}{'Success'};
my $sessionid=$decoded->{'SessionId'};
print "DEBUG: ".$response->content."\n" if($debuglevel>4);
print "DEBUG: ".$success."\n" if($debuglevel>4);
my $i=0;
my $j=0;
my $parameterid;
my $dataValue;
my $rightnow;
my $data = "empty";
while (defined($parameterlist[$i][0])) {
$parameterid=$parameterlist[$i][0];
$request=HTTP::Request->new(GET=>'https://'.$host.'/api/menutree/read_datapoint.json?SessionId='.$sessionid.'&Id='.$parameterid);
$response=$ua->request($request);
$decoded=JSON->new->utf8->decode($response->decoded_content( charset => 'none'));
$success=$decoded->{'Result'}{'Success'};
$dataValue=encode('UTF-8', $decoded->{'Data'}{'Value'});
$parameterlist[$i][3]=&trim($dataValue);
if ($parameterlist[$i][0]==$parameterstatusHeatPump) {
$j=0;
while (defined($statusHeatPumpmatrix[$j][0])) {
if ($statusHeatPumpmatrix[$j][0] eq $parameterlist[$i][3]) {
$parameterlist[$i][3]=$statusHeatPumpmatrix[$j][1];
print "DEBUG: Substituting text of HeatPump\n" if($debuglevel>0);
};
$j++;
}
}
print "DEBUG: ".$response->content."\n" if($debuglevel>4);
print "DEBUG: ".$success."\n" if($debuglevel>4);
print "DEBUG: ".$parameterlist[$i][1]."=".$dataValue."\n" if($debuglevel>0);
$rightnow=localtime->strftime('%Y-%m-%d %H:%M:%S');
if ($data eq "empty"){
$data = $parameterlist[$i][2].':'.$parameterlist[$i][3];
}
else{
$data = $parameterlist[$i][2].':'.$parameterlist[$i][3].','.$data;
}
$i++;
}
print "JSON data = ".$data."\n" if($debuglevel>0);;
#Post data
my $req=HTTP::Request->new(POST=>'http://cms.org/input/post.json?apikey='.$cmsapi.'&node='.$cmsnode.'&json={'.$data.'}');
my $resp = $ua->request($req);
if ($resp->is_success) {
my $message = $resp->decoded_content;
print "Received reply: $message\n" if($debuglevel>0);
}
else {
print "HTTP POST error code: ", $resp->code, "\n" if($debuglevel>0);
print "HTTP POST error message: ", $resp->message, "\n" if($debuglevel>0);
}
}
print "DEBUG: *** Script ended ***\n\n" if($debuglevel>0);
I am answering the specific:
Is there try/cache like in JS?
Yes there is. Instead of
try {
possible evil code;
} catch (e) {
...
}
in perl you write
eval {
possible evil code;
};
if ($#) {
...
}
where $# is the message with which youre code died. BTW - don't vorget the ';' after the eval code.
HTH
Georg
In Perl you can use eval,
For Perl Script:
eval {
your code statement;
}
if($#){
print qq{Error: $#};
}
For CGI file use like below if you want to print the error:
eval {
your code statement || die "Error: $!";
}
if($#){
print qq{Error: $#};
}

control flow validation in perl

This is my code which has switch statement works fine but if user enters greater than 3 or blank text then it should stay in first sub routine how can i do this in perl
use strict;
use warnings;
use Switch;
my $input = "Enter the number:";
sub input(){
print "Choose You Input Method"."\n";
print "1.UPC"."\n";
print "2.URL"."\n";
print "3.Elastic Search"."\n";
print $input;
$input = <>;
chomp($input);
switch($input){
case 1 {print "UPC"."\n"}
case 2 {print "URL"."\n"}
case 3 {print "Elastic Search"."\n"}
else {print "Enter the correct value"."\n"}
}
}
input();
my $pinput = "Enter the number:";
sub pinput(){
print "Choose Your Process Method"."\n";
print "1.API"."\n";
print "2.Mongo"."\n";
print $pinput;
$pinput = <>;
chomp($pinput);
switch($pinput){
case 1 {print "API"."\n"}
case 2 {print "MONGO"."\n"}
else {print "Enter the correct value"."\n"}
}
}
pinput();
if user enters something like 4 or blank data it should not pass to another sub routine it should stay on the same sub routine how can i do this?
Wrap the prompt code into a block with redo:
#!/usr/bin/perl
use warnings;
use strict;
use Switch::Plain;
PROMPT: {
chomp(my $input = <>);
nswitch ($input) {
case 1 : { print "UPC\n" }
case 2 : { print "URL\n" }
case 3 : { print "Elastic Search\n" }
default : { print "Enter the correct value\n" ; redo PROMPT }
}
}
I used Switch::Plain instead of Switch, as it is much safer (it doesn't use a source filter) and sufficient for your case.
perlfaq7 - How do I create a switch or case statement?
Using the builtin function since 5.10
use 5.010;
use strict;
use warnings;
PROMPT: {
chomp(my $input = <>);
given ( $input ) {
when( '1' ) { say "UPC" }
when( '2' ) { say "URL" }
when( '3' ) { say "Elastic Search" }
default { print "Enter the correct value"; redo PROMPT }
};
}

Test for existence of perl mod inside script

Based on the answer provided here, I am attempting to validate whether or not a perl module is installed.
For this, I have:
# &getYN and &prompt are only included here for completeness
sub getYN {
unless ( $autoyes =~ /[Yy]/ ) {
my ( $prompt, $default ) = #_;
my $defaultValue = $default ? "[$default]" : "";
print "$prompt $defaultValue: ";
chomp( my $input = <STDIN> );
return $input ? $input : $default;
} else {
return "Y";
}
}
sub prompt {
my ( $prompt, $default ) = #_;
my $defaultValue = $default ? "[$default]" : "";
print "$prompt $defaultValue: ";
chomp( my $input = <STDIN> );
return $input ? $input : $default;
}
&chklib("RRDTool::OO");
sub chklib {
my $lib = shift;
eval { require $lib; };
if ($#) {
print "You are missing a required Perl Module: $lib\n";
my $ok = &getYN( "Shall I attempt to install it for you?", "y" );
if ( $ok =~ /[Yy]/ ) {
require CPAN;
CPAN::install($lib);
} else {
print "Installation requires $lib\n";
exit;
}
}
}
This runs as expected, but for some reason, the eval returns that I don't have RRDTool::OO installed, when, in fact, I do.
If I create an empty file and run:
# File foo.pl
use strict;
$| = 1;
use RRDTool::OO;
Then I get no errors.
But when I run the first file with print $#;, it returns:
Can't locate RRDTool::OO in ...
What am I doing wrong?
You have to check the result of the eval, like
if (eval("require xxx;")) {
print "you have it\n";
} else {
print "you don't\n";
}
What is happening is that
$lib = "RRDTool::OO";
eval { require $lib }
is executed with the stringified expression
require "RRDTool::OO"
not the bareword style
require RRDTool::OO
so it is looking for a file called RRDTool::OO in your #INC path instead of a file called RRDTool/OO.pm.
If you want to use require at run-time with a variable expression, you'll want to either use the stringy form of eval
eval "require $lib"
or process the arg to require yourself
$lib = "RRDTool::OO";
$lib =~ s{::}{/}g;
eval { require "$lib.pm" }

My TOC script is not generating Strict html standard code

I'd written a Perl script to generate a table of contents from HTML pages which is working fine (and generating valid HTML) except for that the Perl output is removing closing tags for some elements like p. This is not validating against DocType of strict.
Please scroll down the post to see the Perl code.
What should I do to correct it?
#!/usr/bin/perl -w
#Copyright anurag gupta ; free to use under GNU GPL License
use strict;
use feature "switch";
use Common;
use HTML::Element;
use HTML::TreeBuilder;
#"F:/anurag/work/indiacustomercare/airtel/recharge.html";
my $filename="F:/tmp/t9.html";
my $index=0;
my $labelprefix="anu555ltg-";
my $tocIndex=100001;
my $toc;
my #stack;
my $prevHtag="h2";
sub hTagEncountered($)
{
my $hTag=shift;
my $currLevel=(split //, $hTag)[1];
given($hTag)
{
when(/h1/)
{
break;
}
default{
my $countCurr= (split /h/,$hTag)[1];
my $countPrev= (split /h/,$prevHtag)[1];
if($countCurr>$countPrev)
{
push #stack,($currLevel);
$toc.="<ul>";
}
elsif($countCurr<$countPrev)
{
# Now check in the stack
while ( #stack and $currLevel < $stack[$#stack])
{
pop #stack;
$toc.="</ul>";
}
}
}
}
$prevHtag=$hTag;
}
sub getLabel
{
my $name=$labelprefix.++$tocIndex;
}
sub traversehtml
{
my $node=$_[0];
# $node->dump();
# print "-----------------\n";
# print $node->tag()."\n";
# print ref($node),"->\n";
if((ref(\$node) ne "SCALAR" )and ($node->tag() =~m/^h[2-7]$/i)) #it's an H Element!
{
my #h = $node->content_list();
if(#h==1 and ref(\$h[0]) eq "SCALAR") #H1 contains simple string and nothing else
{
hTagEncountered($node->tag());
my $label=getLabel();
my $a = HTML::Element->new('a', name => $label);
my $text=$node->as_trimmed_text();
$a->push_content($text);
$node->delete_content();
$text=HTML::Entities::encode_entities($text);
$node->push_content($a);
$toc.=<<EOF;
<li>$text
EOF
}
elsif ( #h==1 and ($h[0]->tag() eq "a")) # <h1>ttt</h1> case
{
#See if any previous label already exists
my $prevlabel = $h[0]->attr("name");
$h[0]->attr("name",undef) if(defined($prevlabel) and $prevlabel=~m/$labelprefix/); #delete previous name tag if any
#set the new label
my $label=getLabel();
$h[0]->attr("name",$label);
hTagEncountered($node->tag());
my $text=HTML::Entities::encode_entities($node->as_trimmed_text());
$toc.=<<EOF;
<li>$text
EOF
}
elsif (#h>1) #<h1>some text herettt</h1> case
{
die "h1 must not contain any html elements";
}
}
my #h = $node->content_list();
foreach my $item (#h)
{
if(ref(\$item) ne "SCALAR") {traversehtml($item); } #skip scalar items
}
}
die "File $filename not found" if !-r $filename;
my $tree = HTML::TreeBuilder->new();
$tree->parse_file($filename);
my #h = $tree->content_list();
traversehtml($h[1]);
while(pop #stack)
{
$toc.="</ul>";
}
$toc="<ul>$toc</ul>";
print qq{<div id="icctoc"><h2>TOC</h2>$toc</div>};
my #list1=$tree->content_list();
my #list2=$list1[1]->content_list();
for(my $i=0;$i<#list2;++$i){
if(ref(\$list2[$i]) eq "SCALAR")
{
print $list2[$i]
}
else{
print $list2[$i]->as_HTML();
}
}
# Finally:
Try passing {} for the \%optional_end_tags argument to as_HTML. See the documentation for details.

How can I still get automatic assignment to '$_' with a mocked 'readline' function?

Perl has some special handling for the readline function (and the equivalent <> I/O operator) where it treats the expressions
while (<HANDLE>)
while (readline(HANDLE))
as equivalent to
while (defined($_ = <HANDLE>))
cf.
$ perl -MO=Deparse -e 'f($_) while <>'
f($_) while defined($_ = <ARGV>); <--- implicitly sets $_
-e syntax OK
But this automatic assignment doesn't seem to happen if you hijack the readline function:
$ perl -MO=Deparse -e 'BEGIN {
> *CORE::GLOBAL::readline = sub { }
> }
> f($_) while <>'
sub BEGIN {
*CORE::GLOBAL::readline = sub {
};
}
f($_) while readline(ARGV); <--- doesn't set $_ !
-e syntax OK
Of course, this will make the custom readline function work incorrectly for a lot of legacy code. The output of this code is "foo" with the BEGIN block and "bar" without it, but I want it to be "BAR".
use warnings;
BEGIN { *CORE::GLOBAL::readline = \&uc_readline; }
sub uc_readline {
my $line = CORE::readline(shift || *ARGV);
return uc $line if defined $line;
return;
}
($_, $bar) = ("foo\n", "bar\n");
open X, '<', \$bar;
while (<X>) {
print $_; # want and expect to see "BAR\n"
}
What options do I have to hijack the readline function but still get the proper treatment of the while (<...>) idiom? It's not practical to explicitly convert everything to while (defined($_=<...>)) in all the legacy code.
This is a fairly dirty hack using overloading to detect boolean context, but it seems to do the trick. It certainly needs more testing than I have given it before using this solution in a production environment:
use warnings;
BEGIN { *CORE::GLOBAL::readline = \&uc_readline; }
sub uc_readline {
my $line = CORE::readline(shift || *ARGV);
return Readline->new(uc $line) if defined $line;
return;
}
{package Readline;
sub new {shift; bless [#_]}
use overload fallback => 1,
'bool' => sub {defined($_ = $_[0][0])}, # set $_ in bool context
'""' => sub {$_[0][0]},
'+0' => sub {$_[0][0]};
}
my $bar;
($_, $bar) = ("foo\n", "bar\n");
open X, '<', \$bar;
while (<X>) {
print $_; # want and expect to see "BAR\n"
}
which prints:
BAR
This will also make if (<X>) {...} set $_. I don't know if there is a way to limit the magic to only while loops.
This code:
use warnings;
BEGIN { *CORE::GLOBAL::readline = \&uc_readline; }
sub uc_readline {
my $line = CORE::readline(shift || *ARGV);
return unless defined $line;
$line = uc $line;
$_ = $line;
return $line;
}
($_, $bar) = ("foo\n", "bar\n");
open X, '<', \$bar;
while (<X>) {
print $_; # want and expect to see "BAR\n"
}
print "$_"; # prints "BAR" instad of "foo"
does almost the right thing, but $_ is not localised, so after the loop, $_ is set to the last value read from the filehandle. Adding Scope::Upper to the mix fixes that:
use warnings;
use Scope::Upper qw/localize SCOPE/;
BEGIN { *CORE::GLOBAL::readline = \&uc_readline; }
sub uc_readline {
my $line = CORE::readline(shift || *ARGV);
return unless defined $line;
$line = uc $line;
local $_ = $line;
# localize $_ in the scope of the while
localize *main::_, \$line, SCOPE(1);
return $line;
}
($_, $bar) = ("foo\n", "bar\n");
open X, '<', \$bar;
while (<X>) {
print "$_"; # want and expect to see "BAR\n"
}
print "$_"; # will print 'foo', not "BAR"