How to read the properties file from perl script - perl

I have written a perl script where I will be connecting to database, for which I'm using this statement
my $dbh = DBI->connect(
"DBI:mysql:database=test;host=localhost;mysql_socket=/var/run/mysqld/mysqld.sock",
"root", "password", {'RaiseError' => 1});
As I don't want any information to be hardcoded, I want to use properties file where I can list the above details (e.g., database, host, mysql_socket) and read the details of properties file from the script. How can I write the properties file and read the details from perl script?

There are a lot of CPAN modules that helps you to achieve this task.
I like Config::Simple, for example:
#!/usr/bin/perl
use strict;
use warnings;
use Config::Simple;
...
my $cfg = new Config::Simple('myapp.ini');
my $user = $cfg->param('database.user');
my $connection_str = $cfg->param('database.connection');
#...
and the file myapp.ini:
[database]
connection="DBI:mysql:database=test;host=localhost;mysql_socket=/var/run/mysqld/mysqld.sock"
user=root
;...
You can install the module from the terminal/command prompt using:
cpan install Config::Simple
or
yum install perl-Config-Simple

I had issues using perl-Config-Simple and decided to use Config::Properties instead. If you are experiencing the same, then you can try the following.
Make sure you have the Config::Properties installed. The following are several examples of how to install from command line, depending on the OS you are using, you'll want to use the appropriate choice:
cpan Config::Properties
cpan install Config::Properties
yum install perl-Config-Properties
The code:
#!/usr/bin/perl
use strict;
use warnings;
use Config::Properties;
open my $cfh, '<', './foo.properties' or die "unable to open property file";
my $properties = Config::Properties->new();
$properties->load($cfh);
my $dbName = $properties->getProperty('database.name');
my $dbUser = $properties->getProperty('database.user');
The property file:
database.name=somedb
database.user=someuser
Once you have the values in the variables, put them into your connection string and you should be good to go.

var temp;
if($ENV eq "Prod"){
#Prod Configurations
temp = "Prod";
}
else{
# Stage and Test Confgurations
temp = "Stage";
}

My brick and mortar solution, exports one value from a properties file:
#!/usr/bin/perl
use strict;
use warnings;
my $line;
foreach $line (<STDIN>) {
chomp($line);
if(my $match = $line =~ /^(.*)=(.*)$/){
my $key = $1;
my $value = $2;
if ($ARGV[0] eq $key) {
print "$value\n";
exit 0;
}
}
}
Usage: perl script.pl mykey < file.properties

Related

perl system command execution

In the following code I need to print output from the variables $stout, $stderr. How can I do this and not without using $ssh = Net::SSH::Perl->new?
#!/usr/bin/perl
use strict;
use warnings;
use Net::SSH::Perl;
use FindBin qw($RealBin);
use File::Basename;
use lib "/nfs/site/proj/dpg/tools/lib/perl";
use Util;
use Getopt::Std;
use Net::SSH::Perl;
use Cwd;
use File::Copy;
my $host = 'someip.com.com';
my $pass = '';
my $user = '';
my $cmd = 'pwd';
my($stdout,$stderr,$exit) = system('ssh someip.com cat /nfs/site/home/aa/test11.txt') ;
if ($stdout) {
print "Standard output is \n$stdout";
} elsif($stderr) {
print "Standard error is \n$stderr";
}
I use IO::CaptureOutput, its simpler.
use strict;
use warnings;
use IO::CaptureOutput qw(capture_exec);
my $cmd = "ssh someip.com cat /nfs/site/home/aa/test11.txt";
my ($stdout, $stderr, $success, $exitcode) = capture_exec( $cmd );
You can also use list parameters in capture_exec which I think is safer.
open3 allows you to read/write all handles:
use FileHandle;
use IPC::Open3;
my $cmd = "ssh someip.com cat /nfs/site/home/aa/test11.txt";
open3(\*GPW, \*GPR, \*GPE, "$cmd") or die "$cmd";
# print GPW "write to ssh";
close GPW;
while (<GPR>) {
# read stdout
}
while (<GPE>) {
# read stderr
}
close GPR; close GPE;
I've always been a fan of IPC::Run:
use IPC::Run;
my $exitcode = run [ "ssh", "someip.com", "cat", ... ],
undef, \my $stdout, \my $stderr;
At this point the STDOUT and STDERR results from the command will be stored in those two lexicals.
Though as a solution to the general issue of ssh'ing to a host and retrieving the contents of a file from within a Perl program, you might like IPC::PerlSSH:
use IPC::PerlSSH;
my $ips = IPC::PerlSSH->new( Host => "someip.com" );
$ips->use_library( "FS", qw( readfile ) );
my $content = $ips->call( "readfile", "/nfs/site/home/aa/test11.txt" );
The $ips object here will just hang around and allow for reuse to collect multiple files, execute other commands, and generally reuse the connection, rather than having to set up a new ssh connection every time.

How can I list the contents of a ZIP file using Perl?

What I am aiming to do is pretty much what it says in the title.
I have the following line of code which simply prints out [view archive] and when I click it the browser just downloads the zip file.
print "\<a href=\"http:\/\/intranet.domain.com\/~devcvs\/view-file.cgi?file=$reviewdata{'document'}&review_id=$reviewdata{'id'}\"\>[view archive]\<\/a\>\n";
What I would love to do is to list the files contained within this zip file anywhere on the page, e.g. just underneath or even a new page which this link links to and takes the filename as a parameter.
I believe once this is done the browser should take care of the rest in terms of just clicking these files and viewing them in the browser as they will be pdfs and html files which I don't foresee any problems with.
I am sure there is a module that does this but I am unsure of how to accomplish my goal using it.
Any help is much appreciated.
Have a look at Archive::Zip :
use strict;
use warnings;
use Archive::Zip qw/ :ERROR_CODES :CONSTANTS /;
my $zipFile = 'someZip.zip';
my $zip = Archive::Zip->new();
unless ( $zip->read( $zipFile ) == AZ_OK ) { # Make sure archive got read
die 'read error';
}
my #files = $zip->memberNames(); # Lists all members in archive
print $_, "\n" for #files;
Using Archive::Zip certainly makes the code easier, and you should probably install that module if you are going to work extensively on zip files.
However, for those who prefer not to install anything, there is a way to list the content of a zip file just using the core module IO::Uncompress::Unzip (already part of any standard Perl distribution).
use strict;
use warnings;
use IO::Uncompress::Unzip qw($UnzipError);
my $zipFile = '/path/to/zipfile.zip';
my $u = IO::Uncompress::Unzip->new($zipFile)
or die "Error: $UnzipError\n";
my $status;
for ($status = 1; $status > 0; $status = $u->nextStream()) {
my $header = $u->getHeaderInfo();
my $zippedFile = $header->{Name};
if ($zippedFile =~ /\/$/) {
last if $status < 0;
next;
}
print "$zippedFile\n";
}

How do I access a hash from another subroutine?

I am trying to create some scripts for web testing and I use the following piece of code to set up variables from a config file:
package setVariables;
sub readConfig{
open(FH, "workflows.config") or die $!;
while(<FH>)
{
($s_var, $s_val) = split("=", $_);
chomp($s_var);
chomp($s_val);
$args{$s_var} = $s_val;
print "set $s_var = $s_val\n";
}
close(FH);
}
for example: var1=val1
var2=val2
var3=val3
etc...
I want to be able to pass the values set by this subroutine to a subroutine in another package. This is what I have for the package I want it passed into.
package startTest;
use setVariables;
sub startTest{
my %args = %setVariables::args;
my $s_var = $setVariables::s_var;
my $s_val = $setVariables::s_var;
setVariables::readConfig(); #runs the readConfig sub to set variables
my $sel = Test::WWW::Selenium->new( host => "localhost",
port => 4444,
browser => $args{"browser"},
browser_url => $args{"url"} );
$sel->open_ok("/index.aspx");
$sel->set_speed($args{"speed"});
$sel->type_ok("userid", $args{"usrname"});
$sel->type_ok("password", $args{"passwd"});
$sel->click_ok("//button[\#value='Submit']");
$sel->wait_for_page_to_load_ok("30000");
sleep($args{"sleep"});
}
Unfortunately its not holding on to the variables as is and I don't know how to reference them.
Thank you for any help.
Your code has some problems. Let's fix those first.
# Package names should start with upper case unless they are pragmas.
package SetVariables;
# Do this EVERYWHERE. It will save you hours of debugging.
use strict;
use warnings;
sub readConfig{
# Use the three argument form of open()
open( my $fh, '<', "workflows.config")
or die "Error opening config file: $!\n";
my %config;
# Use an explicit variable rather than $_
while( my $line = <$fh> )
{
chomp $line; # One chomp of the line is sufficient.
($s_var, $s_val) = split "=", $line;
$config{$s_var} = $s_val;
print "set $s_var = $s_val\n";
}
close $fh;
return \%config;
}
Then use like so:
use SetVariables;
my $config = SetVariables::readConfig();
print "$_ is $config->{$_}\n"
for keys %$config;
But rather than do all this yourself, check out the many, many config file modules on CPAN. Consider Config::Any, Config::IniFiles, Config::JSON.
You note in your comment that you are trying to work with multiple files, your main code and a couple of packages.
One pattern that is common is to load your config in your main code and pass it (or select elements of it) to consuming code:
package LoadConfig;
sub read_config {
my $file = shift;
my $config;
# Do stuff to read a file into your config object;
return $config;
}
1;
Meanwhile in another file:
package DoStuff;
sub run_some_tests {
my $foo = shift;
my $bar = shift;
# Do stuff here
return;
}
sub do_junk {
my $config;
my $foo = $config->{foo};
# Do junk
return;
}
1;
And in your main script:
use DoStuff;
use LoadConfig;
my $config = LoadConfig::read_config('my_config_file.cfg');
run_some_tests( $config->{foo}, $config->{bar} );
do_junk( $config );
So in run_some_tests() I extract a couple elements from the config and pass them in individually. In do_junk() I just pass in the whole config variable.
Are your users going to see the configuration file or just programmers? If it's just programmers, put your configuration in a Perl module, then use use to import it.
The only reason to use a configuration file for only programmers if you are compiling the program. Since Perl programs are scripts, don't bother with the overhead of parsing a configuration file; just do it as Perl.
Unless it's for your users and its format is simpler than Perl.
PS: There's already a module called Config. Call yours My_config and load it like this:
use FindBin '$RealBin';
use lib $RealBin;
use My_config;
See:
perldoc FindBin
perldoc Config
I would suggest using a regular format, such as YAML, to store the configuration data. You can then use YAML::LoadFile to read back a hash reference of the configuration data and then use it.
Alternatively, if you don't want to use YAML or some other configuration format with pre-written modules, you'll need for your reading routine to actually return either a hash or a a hashref.
If you need some more background information, check out perlref, perlreftut and perlintro.
all you need to do is collect the variable in a hash and return a reference to it in readConfig:
my %vars = ( var1 => val1,
var2 => val2,
var3 => val3,
);
return \%vars;
and in startTest:
my $set_vars = setVariables::readConfig();

Adding an Include Path for a Module in a Simple Perl Tutorial

I'm trying to do a simple tutorial but I'm having trouble getting started. My problem seems to be installing and getting the correct path to the modules.
**1. Here is the original code:*****
#!/usr/bin/perl -w
use strict;
use LWP 5.64;
my $browser = LWP::UserAgent->new;
my $url = 'http://www.cspan.org/RECENT.html';
my $response = $browser->get($url);
die "Can't get $url -- ", $response->status_line
unless $response->is_success;
my $html = $response->content;
while( $html =~m/<A HREF=\"(.*?)\"/g ) {
print "$1\n";
2. But in Host Gator they say this:
Location of Your Perl Module(s)
Path: /home/d********n/perl
Using Your Perl Module(s)
You will need to add /home/d********n/perl to the include path. You can do this by adding the following code to your script:
BEGIN {
my $base_module_dir = (-d '/home/d********n/perl' ? '/home/d********n/perl' : ( getpwuid($>) )[7] . '/perl/');
unshift #INC, map { $base_module_dir . $_ } #INC;
}
3. So I added the code but have no idea if I added it in the correct spot.
#!/usr/bin/perl -w
use strict;
use LWP 5.64;
BEGIN {
my $base_module_dir = (-d '/home/d********n/perl' ?
'/home/d********n/perl' : ( getpwuid($>) )[7] . '/perl/');
unshift #INC, map { $base_module_dir . $_ } #INC;
}
my $browser = LWP::UserAgent->new;
my $url = 'http://www.cspan.org/RECENT.html';
my $response = $browser->get($url);
die "Can't get $url -- ", $response->status_line
unless $response->is_success;
my $html = $response->content;
while( $html =~m/<A HREF=\"(.*?)\"/g ) {
print "$1\n";
Any help would be greatly appreciated.
FYI, I already made sure the file has the needed permissions 755
Also the LWP::UserAgent has a number of 5.835 in Host Gator. Does that mean I have to change
use LWP 5.64;
to
use LWP 5.835
Assuming you've got LWP installed in your local module directory, put the BEGIN block before you try to load LWP (right after use strict).
The version number in the original code indicates that it's the minimum required version. Since you've got a newer version and LWP's interface is stable, a simple use LWP; will suffice.
The solution Host Gator seems a bit complicated. I would use the lib module :
use strict ;
use lib '/home/d********n/perl' ;
use LWP ;
If you are running the script from a command line there are two ways you can run it unchanged.
Set it as an environment variable by typing following at command line :
export PERL5LIB=/home/d********n/perl
myscript.pl
or add it as an option to the perl commaind
perl -I/home/d********n/perl myscript.pl
Or directly on the command line using perl option parameter flag -I for eg. multiple directories/projects also with a general module
perl -I'../project/lib' -I'../otherProject/lib' -I'lib' -M'Test::Doctest' -e run lib/MyOwnModule.pm

How can I get user input without waiting for enter in Perl?

I am trying to make an interactive shell script in Perl.
The only user input I can find is the following:
$name = <STDIN>;
print STDOUT "Hello $name\n";
But in this the user must always press enter for the changes to take effect.
How can I get the program to proceed immediately after a button has been pressed?
From perlfaq8's answer to How do I read just one key without waiting for a return key?
:
Controlling input buffering is a remarkably system-dependent matter. On many systems, you can just use the stty command as shown in getc in perlfunc, but as you see, that's already getting you into portability snags.
open(TTY, "+</dev/tty") or die "no tty: $!";
system "stty cbreak </dev/tty >/dev/tty 2>&1";
$key = getc(TTY); # perhaps this works
# OR ELSE
sysread(TTY, $key, 1); # probably this does
system "stty -cbreak </dev/tty >/dev/tty 2>&1";
The Term::ReadKey module from CPAN offers an easy-to-use interface that should be more efficient than shelling out to stty for each key. It even includes limited support for Windows.
use Term::ReadKey;
ReadMode('cbreak');
$key = ReadKey(0);
ReadMode('normal');
However, using the code requires that you have a working C compiler and can use it to build and install a CPAN module. Here's a solution using the standard POSIX module, which is already on your system (assuming your system supports POSIX).
use HotKey;
$key = readkey();
And here's the HotKey module, which hides the somewhat mystifying calls to manipulate the POSIX termios structures.
# HotKey.pm
package HotKey;
#ISA = qw(Exporter);
#EXPORT = qw(cbreak cooked readkey);
use strict;
use POSIX qw(:termios_h);
my ($term, $oterm, $echo, $noecho, $fd_stdin);
$fd_stdin = fileno(STDIN);
$term = POSIX::Termios->new();
$term->getattr($fd_stdin);
$oterm = $term->getlflag();
$echo = ECHO | ECHOK | ICANON;
$noecho = $oterm & ~$echo;
sub cbreak {
$term->setlflag($noecho); # ok, so i don't want echo either
$term->setcc(VTIME, 1);
$term->setattr($fd_stdin, TCSANOW);
}
sub cooked {
$term->setlflag($oterm);
$term->setcc(VTIME, 0);
$term->setattr($fd_stdin, TCSANOW);
}
sub readkey {
my $key = '';
cbreak();
sysread(STDIN, $key, 1);
cooked();
return $key;
}
END { cooked() }
1;
You can use the Term::ReadKey module to check for a keypress.