Strip Pod as Pod from Perl file - perl

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;

Related

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)

possible for perl to print escape codes and not leave echo -n ""

With many xterm's its nice to have a unique name on the window border that I can change on the fly. So I wrote this perl code in file header.pl:
#!/usr/bin/perl
my $saywhat = $ARGV[0];
my $mycmnd = <<"EOLzippo";
echo -n "\033]0;XROSS\007"
EOLzippo
$mycmnd =~ s!XROSS!$saywhat!g;
print $mycmnd;
Then with an alias:
alias header 'perl ~/perl/header.pl \!* '
I can change the text at the top of my xterms.
When the alias is called the code leaves a echo-n on my command line:
163 perl> header test
echo -n ""
Is there a way to execute the print without leaving the echo -n ""?
There's no need to print echo (did you try to shell out and echo?). print already prints.
#!/usr/bin/perl
use warnings;
use strict;
my $saywhat = shift;
my $string = "\033]0;$saywhat\007";
print $string;
Do it all in perl:
#!/usr/bin/perl
my $saywhat = $ARGV[0];
my $mycmnd = "\033]0;${saywhat}\007"
print $mycmnd;
Side note: While it is a convention to use a .pl suffix sometimes, it is by no means mandatory. If you rename header.pl to header and put it in ~/bin [or add ~/perl to $PATH] you can eliminate the alias. Just be sure to set execute permissions on the file (e.g. 755)

From inside a perl script can you know the name of the file you are redirecting output to?

So I have:
test.pl > test.log
is there a way to know inside test.pl that I am outputing to 'test.log'? At the end of my script I want to do some manipulation of test.log without hardcoding the name.
Maybe. The following works on Linux, but will not be very portable to other systems...
#!/usr/bin/env perl
use strict;
use warnings;
my $out = readlink("/proc/$$/fd/1");
print STDERR "I am being output to $out\n";
Naturally, this is probably a bad idea. Better to explicitly open the file and write to it in Perl, rather than having the shell set up redirections.
You can redirect standard output from perl, with minimal changes to your script,
test.pl test.log
my ($file) = #ARGV;
if (#ARGV) {
open STDOUT, ">", $file or die $!;
}
print "output is redirected to $file\n";
# use $file at the end

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.

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

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.