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

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.

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 get trace output from only the main package of a Perl program

I would like to trace my Perl script, but only code in the main package, and redirect output to a file.
When starting the script with perl -d script.pl it goes into interactive mode.
I have tried
perl -d:Trace perl_05.pl 2&> output.log
But this also traces all subroutines and modules which I do not want.
I am looking for something like the bash set -o xtrace or sh -x.
The article Trace your Perl programs addresses your concerns regarding the amount of traces and shows you a way to tune the output so you just trace what you want.
You can create your own throw-away module to trace exactly what you want. Since the current directory is usually already in #INC, you can create a Devel/MyTrace.pm.
Read it completely to see how the author modifies the default behavior of the trace function to output first the traces to a file, then to STDERR too and finally limits the output to just trace the primary file.
You can go one step further to exclude all code except for the primary file:
use v5.10;
use autodie;
BEGIN {
my $trace_file = $ENV{TRACE_FILE} // "mytrace.$$";
print STDERR "Saving trace to $trace_file\n";
my $fh = do {
if( $trace_file eq '-' ) { \*STDOUT }
elsif( $trace_file eq 'STDERR' ) { \*STDERR }
else {
open my $fh, '>>', $trace_file;
$fh;
}
};
sub DB::DB {
my( $package, $file, $line ) = caller;
return unless $file eq $0;
my $code = \#{"::_<$file"};
print $fh "[#{[time]}] $file $l $code->[$line]";
}
}
1;
That last chunk of code is the one that is specially interesting for you. It does exactly what you want.
Using perl with Devel::DumpTrace is a lot like using bash -x. Like bash -x, Devel::DumpTrace expands and outputs variable values to give you an idea of what your script was doing, not just where it is doing it.
It also has the feature you are looking for: to enable and disable tracing on specific packages. For your use case, you would run it like
perl -d:DumpTrace=-.*,+main my_script.pl
or
perl -d:DumpTrace=-.* my_script.pl
-.* means "exclude all packages that match /^.*$/ from tracing", which is to say, all packages. +main means "include package main in tracing".
The default output can be fairly verbose. If you want less output than that, you can specify quiet mode:
perl -d:DumpTrace=quiet,-.*,+main my_script.pl
(I am the author of Devel::DumpTrace)

Problems using the HTML::Template module

I'm unable to execute the HTML::Template function in the CGI.
I'm following a simple tutorial that I found here: http://metacpan.org/pod/HTML::Template
I created a new file on my server in the home path as test.tmpl.
I created a new file named frt.cgi ... (is that the issue here? should it be a different file extention??)
#!/usr/local/bin/perl -w
use HTML::Template;
# open the html template
my $template = HTML::Template->new(filename => '/test.html');
# fill in some parameters
$template->param(HOME => $ENV{HOME});
$template->param(PATH => $ENV{PATH});
# send the obligatory Content-Type and print the template output
print "Content-Type: text/html\n\n", $template->output;
I've modified the 1st line to reflect my host provided program path for perl. I don't know what the -w does I just know I've tried this with and without it. Also I've tried changing the code a bit like this:
use warnings;
use strict;
use CGI qw(:standard);
use HTML::Template;
I've searched...
https://stackoverflow.com/search?q=HTML%3A%3ATEMPLATE+&submit=search
https://stackoverflow.com/search?q=HTML%3A%3ATEMPLATE
https://stackoverflow.com/search?q=HTML%3A%3ATEMPLATE+PERL&submit=search
Yet I still do not see the answer.
I even searched google for .TMPL Encoding because I thought there may be some special type needed. Please help.
If you look in your server logs, you'll probably see an error message along the lines of:
HTML::Template->new() : Cannot open included file /test.html : file not found.
You need to provide the path on the file system, not a URI relative to the generated document.
First, you likely specified the wrong path - change /test.html to test.html.
Also, it is possible that there is no $ENV{HOME} variable in your system so set up flag die_on_bad_params to 0:
my $template = HTML::Template->new(
filename => 'test.html',
die_on_bad_params => 0,
);
Also, don't forget to mark your Perl file as executable by chmod 755.
Option -w makes Perl to enable warnings, so there is no point to write use warnings; afterwards.
You can check what Perl command line options do by using module B::Deparse, like this ($^W variable disables/enables warnings):
perl -w -MO=Deparse -e "print;"
This would print:
BEGIN { $^W = 1; }
print $_;

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.

perl mktemp and echo

i am trying to put some word in tempfile via commandline
temp file creat but word not past in tempfile
#!/usr/bin/perl -w
system ('clear');
$TMPFILE = "mktemp /tmp/myfile/devid.XXXXXXXXXX";
$echo = "echo /"hello world/" >$TMPFILE";
system ("$TMPFILE");
system ("$echo");
Please Help to Solve This
To capture the name output by mktemp, do this instead:
chomp($TMPFILE = `mktemp /tmp/myfile/devid.XXXXXXXXXX`);
But Perl can do all the things you are doing without resorting to the shell.
Avoid using external commands from perl script as much as possible.
you can use: File::Temp module in this case, see this
Here's a specific demonstration of the advice that others have given you: where possible, use Perl directly rather than invoking system. Also, you should get in the habit of including use strict and use warnings in your Perl scripts.
use strict;
use warnings;
use File::Temp;
my $ft = File::Temp->new(
UNLINK => 0,
TEMPLATE => '/tmp/myfile/devid.XXXXXXXXXX',
);
print "Writing to temp file: ", $ft->filename, "\n";
print $ft "Hello, world.\n";