Perl - en / em dash in command line arguments - perl

I'm having a problem with my perl script with parsing command line arguments. Mainly, I'd like perl to parse argument preceding with (em/en)-dash as well as hypen. Consider the following command execution:
my_spript.pl -firstParam someValue –secondParam someValue2
As you can see, firstParam is prefixed with hypen and there is no problem with perl parsing it, but the secondParam is prefixed with en-dash and unfortunately Perl cannot recognize it as an argument.
I am using GetOptions() to parse arguments:
GetOptions(
"firstParam" => \$firstParam,
"secondParam" => \$secondParam
)

If you're using Getopt::Long, you can preprocess the arguments before giving them to GetOptions:
#! /usr/bin/perl
use warnings;
use strict;
use Getopt::Long;
s/^\xe2\x80\x93/-/ for #ARGV;
GetOptions('firstParam:s' => \ my $first_param,
'secondParam:s' => \ my $second_param);
print "$first_param, $second_param\n";
It might be cleaner to first decode the arguments, though:
use Encode;
$_ = decode('UTF-8', $_), s/^\N{U+2013}/-/ for #ARGV;
To work in different locale setting, use Encode::Locale:
#! /usr/bin/perl
use warnings;
use strict;
use Encode::Locale;
use Encode;
use Getopt::Long;
$_ = decode(locale => $_), s/^\N{U+2013}/-/ for #ARGV;
GetOptions('firstParam:s' => \ my $first_param,
'secondParam:s' => \ my $second_param);
print "$first_param, $second_param\n";

Related

How to replace a String in a file with another string which contains single quotes using perl script

I have a text file in UNIX (say config.cfg) which has data like below.
Key and value seperated by tab.
monitorinput 'false'
inputDir ''
useSsl 'false'
enableSorting 'false'
inputtimeunit 'Day'
I want to update the value of inputDir using perl or shell script.
before update:
inputDir ''
so after update it should be
inputDir '/home/user/pricing/inflow/'
I tried many things. not able to put value within the quotes.
use File::Basename;
use File::Path;
use File::Copy;
use File::Find;
use File::Path qw(make_path remove_tree);
use Getopt::Std;
my $target_conf_file="/home/pricing/config.cfg";
my $node_backup_dir = "/home/user/pricing/inflow/";
#my $commndresult= system("sed -i 's|^inputDir.*|inputDir\t'${node_backup_dir}'|g' $target_conf_file");
my $commndresult= system("sed -i 's|^inputDir.*|inputDir '$node_backup_dir'|g' $target_conf_file");
if ( $commndresult == 0 )
{
print "\nInput Directory updated";
}
There's really no need to drag sed into this when you can just use perl directly. The handy Path::Tiny module makes it trivial:
#!/usr/bin/env perl
use warnings;
use strict;
use Path::Tiny;
my $target_conf_file="/home/pricing/config.cfg";
my $node_backup_dir = "/home/user/pricing/inflow/";
path($target_conf_file)->edit_lines(sub {
$_ = "inputDir\t'${node_backup_dir}'" if /^inputDir\s/
});
or if you can't install an extra module for some reason, (ab)using the behavior of -i (Inspired by this answer):
#!/usr/bin/env perl
use warnings;
use strict;
my $target_conf_file="/home/pricing/config.cfg";
my $node_backup_dir = "/home/user/pricing/inflow/";
our $^I = "";
our #ARGV = ($target_conf_file);
while (<ARGV>) {
$_ = "inputDir\t'${node_backup_dir}'" if /^inputDir\s/;
print;
}
I think this is a typical example of using awk
% <config.cfg awk -v q=\' '$1 == "inputDir" { $2 = q "/home/user/pricing/inflow/" q } { print; }'
monitorinput 'false'
inputDir '/home/user/pricing/inflow/'
useSsl 'false'
enableSorting 'false'
inputtimeunit 'Day'

Why IPC::System::Simple(capture) is not working with arguments

I am trying to call a second script from the main script. When I am passing the argument in the command itself using capture, it's working. But when I am trying to send the command and arguments separately in capture function it's giving me an error that it can't find the specified file.
Second script
#!/usr/bin/perl
use 5.010;
use strict;
use warnings;
my $word= $ARGV[0];
my $crpyt = "$word crypted";
print "$crpyt\n";
my $decrypt = "$word decrypted";
print "$decrypt\n";
main.pl
#!/usr/bin/perl
use 5.010;
use strict;
use warnings;
use IPC::System::Simple qw(capture capturex);
my $cmd= 'perl xyz.pl Hello';
my #arr = capture($cmd);
print "$arr[0]";
print "$arr[1]\n";
This is working
Output:
Hello crypted
Hello decrypted
BUT
main.pl
#!/usr/bin/perl
use 5.010;
use strict;
use warnings;
use IPC::System::Simple qw(capture capturex);
my $cmd= 'perl xyz.pl';
my #arg=("Hello");
my #arr = capture($cmd,#arg);
print "$arr[0]";
print "$arr[1]\n";
This is not working. It says
"perl xyz.pl" failed to start: "The system cannot find the file specified" at main.pl line 11
If you only pass a single scalar, capture expects it to be a shell command.
As such, capture('perl xyz.pl Hello') works.
If you pass multiple scalars, capture expects the first to be a path to a program to execute. The rest are passed as arguments.
As such, capture('perl xyz.pl', 'Hello') doesn't work.
You could use
use IPC::System::Simple qw( capture );
my #cmd = ( 'perl', 'xyz.pl', 'Hello' );
capture(#cmd)
But you never want to use capture unless you pass a single scalar that's a shell command. Use capturex when passing a path and arguments.
use IPC::System::Simple qw( capturex );
my #cmd = ( 'perl', 'xyz.pl', 'Hello' );
capturex(#cmd)
But let's say you get the string perl xyz.pl from elsewhere. A shell needs to be invoked, so we need to convert the arguments in to shell literals.
use IPC::System::Simple qw( capture );
use String::ShellQuote qw( shell_quote );
my $cmd = 'perl xyz.pl';
my #extra_args = 'Hello';
my $full_cmd = $cmd . ' ' . shell_quote(#extra_args);
capture($cmd)
Alternatively,
use IPC::System::Simple qw( capturex );
my $cmd = 'perl xyz.pl';
my #extra_args = 'Hello';
capturex('sh', '-c', 'eval $0 "$#"', $cmd, #extra_args)

Perl substitute some content in a xml file

I am a newbie of Perl. Now i am trying to use Perl to substitute some content in a xml file. the following code is my command
perl -pi -e "s/<Connector port=\"\d+\" protocol=\"HTTP/1.1\" /<Connector port=\"${ACCESS_PORT}\" protocol=\"HTTP/1.1\" /g" $TOMCAT_SERVER_CONF
but perl gives complains this:
Bareword found where operator expected at -e line 1, near ""34233" protocol"
(Missing operator before protocol?)
Can't modify numeric lt (<) in scalar assignment at -e line 1, near ""34233" protocol"
syntax error at -e line 1, near ""34233" protocol"
Execution of -e aborted due to compilation errors.
could anyone help out here? would be very appreciate it.
You have to escape the forward slash before the 1.1 in your command (there tare two same thing in your command in fact). Because you are using / as regex delimiter.
\"HTTP\/1.1\"
^ here
Alternately you can use any different regex delimiter as well. For example using a hash:
s#..regex..#;;replace;;#g
Don't use regex to parse XML. It's nasty. Use a parser instead:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
my $twig = XML::Twig -> parsefile ( $ENV{'TOMCAT_SERVER_CONF'} );
foreach my $connector ( $twig -> get_xpath('Connector') ) {
$connector -> set_att('port', $ENV{'ACCESS_PORT'} );
}
$twig -> print;
If you need an in place edit:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
sub mod_connector {
my ( $twig, $connector ) = #_;
$connector->set_att( 'port', $ENV{'ACCESS_PORT'} );
}
my $twig = XML::Twig->new( twig_handlers => { 'Connector' => \&mod_connector } );
$twig -> parsefile_inplace( $ENV{'TOMCAT_ACCESS_CONF'} );
And if you really want a one liner:
perl -MXML::Twig -e 'XML::Twig->new( twig_handlers => { Connector => sub { $_->set_att( "port", $ENV{ACCESS_PORT} ) }})->parsefile_inplace( $ENV{TOMCAT_ACCESS_CONF} );'

Pass a hash object from one perl script to another using system

I have the following perl script, that takes in a parameters' file and stores it into a hash. I want to modify & pass this hash to another perl script that I am calling using the system command:
script1.pl
#!/usr/bin/perl -w
# usage perl script1.pl script1.params
# script1.params file looks like this:
# PROJECTNAME=>project_dir
# FASTALIST=>samples_fastq.csv
use Data::Dumper;
my $paramfile = $ARGV[0];
# open parameter file
open PARAM, $paramfile or die print $!;
# save it in a hash
my %param;
while(<PARAM>)
{
chomp;
#r = split('=>');
$param{$r[0]}=$r[1];
}
# define directories
# add to parameters' hash
$param{'INDIR'} = $param{'PROJECTNAME'}.'/input';
$param{'OUTDIR'} = $param{'PROJECTNAME'}.'/output';
.... do something ...
# #samples is a list of sample names
foreach (#samples)
{
# for each sample, pass the hash values & sample name to a separate script
system('perl script2.pl <hash> $_');
}
script2.pl
#!/usr/bin/perl -w
use Data::Dumper;
## usage <script2.pl> <hash> <samplename>
# something like getting and printing the hash
my #string = $ARGV[0];
print #string;
If you can help me showing how to pass and get the hash object (something simple like printing the hash object in the second script would do), then I'd appreciate your help.
Thanks!
What you're looking for is something called serialisation. It's difficult to directly represent a memory structure in such a way as to pass it between processes, because of all sorts of fun things like pointers and buffers.
So you need to turn your hash into something simple enough to hand over in a single go.
Three key options for this in my opinion:
Storable - a perl core module that lets you freeze and thaw a data structure for this sort of purpose.
JSON - a text based representation of a hash-like structure.
XML - bit like JSON, but with slightly different strengths/weaknesses.
Which you should use depends a little on how big your data structure is.
Storable is probably the simplest, but it's not going to be particularly portable.
There's also Data::Dumper that's an option too, as it prints data structures. Generally though, I'd suggest that has all the downsides of all the above - you still need to parse it like JSON/XML but it's also not portable.
Example using Storable:
use strict;
use warnings;
use Storable qw ( freeze );
use MIME::Base64;
my %test_hash = (
"fish" => "paste",
"apples" => "pears"
);
my $frozen = encode_base64 freeze( \%test_hash );
system( "perl", "some_other_script.pl", $frozen );
Calling:
use strict;
use warnings;
use Storable qw ( thaw );
use Data::Dumper;
use MIME::Base64;
my ($imported_scalar) = #ARGV;
print $imported_scalar;
my $thing = thaw (decode_base64 $imported_scalar ) ;
print Dumper $thing;
Or:
my %param = %{ thaw (decode_base64 $imported_scalar ) };
print Dumper \%param;
This will print:
BAoIMTIzNDU2NzgEBAQIAwIAAAAKBXBhc3RlBAAAAGZpc2gKBXBlYXJzBgAAAGFwcGxlcw==
$VAR1 = {
'apples' => 'pears',
'fish' => 'paste'
};
Doing the same with JSON - which has the advantage of being passed as plain text, and in a general purpose format. (Most languages can parse JSON):
#!/usr/bin/env perl
use strict;
use warnings;
use JSON;
my %test_hash = (
"fish" => "paste",
"apples" => "pears"
);
my $json_text = encode_json ( \%test_hash );
print "Encoded: ",$json_text,"\n";
system( "perl", "some_other_script.pl", quotemeta $json_text );
Calling:
#!/usr/bin/env perl
use strict;
use warnings;
use JSON;
use Data::Dumper;
my ($imported_scalar) = #ARGV;
$imported_scalar =~ s,\\,,g;
print "Got: ",$imported_scalar,"\n";
my $thing = decode_json $imported_scalar ;
print Dumper $thing;
Need the quotemeta and the removal of slashes unfortunately, because the shell interpolates them. This is the common problem if you're trying to do this sort of thing.

Passing hash reference as an argument to perl script from perl script

I want to pass a hash reference as an argument from one perl script (script1.pl) to another perl script (script2.pl). This is how my code looks:
----------------------------script1.pl---------------------------------
#!/usr/bin/perl -w
use strict;
use warnings;
my %hash = (
'a' => "Harsha",
'b' => "Manager"
);
my $ref = \%hash;
system "perl script2.pl $ref";
----------------------------script2.pl---------------------------------
#!/usr/bin/perl -w
use strict;
use warnings;
my %hash = %{$ARGV[0]};
my $string = "a";
if (exists($hash{$string})){
print "$string = $hash{$string}\n";
}
And this is the output error:
sh: -c: line 0: syntax error near unexpected token `('
sh: -c: line 0: `perl script2.pl HASH(0x8fbed0)'
I can't figure out the right way to pass the reference.
A hash is an in memory data structure. Processes 'own' their own memory space, and other processes can't just access it. If you think about it, I'm sure you'll spot why quite quickly.
A hash reference is an address of that memory location. Even if the other process could 'understand' it, it still wouldn't be able to access the memory space.
What we're talking about here is actually quite a big concept - Inter Process Communication or IPC - so much so there's a whole chapter of the documentation about it, called perlipc.
The long and short of it is this - you can't do what you're trying to do. Sharing memory between processes is much more difficult than you imagine.
What you can do is transfer the data back and forth - not by reference, but the actual information contained.
I would suggest that for your example, the tool for the job is JSON, because then you can encode and decode your hash:
#!/usr/bin/perl -w
use strict;
use warnings;
use JSON;
my %hash = (
'a' => "Harsha",
'b' => "Manager"
);
my $json_string = to_json( \%hash );
print $json_string;
This gives:
{"b":"Manager","a":"Harsha"}
Then your can 'pass' your $json_string - either on the command line, although bear in mind that any spaces in it confuses #ARGV a bit if you're not careful - or via STDIN.
And then decode in your sub process:
use strict;
use warnings;
use JSON;
my $json_string = '{"b":"Manager","a":"Harsha"}';
my $json = from_json ( $json_string );
my $string = "a";
if (exists($json -> {$string} )){
print "$string = ",$json -> {$string},"\n";
}
(You can make it more similar to your code by doing:
my $json = from_json ( $json_string );
my %hash = %$json;
Other options would be:
use Storable - either freezing and thawing ( memory) or storing and retrieving (disk)
use IPC::Open2 and send data on STDIN.
There's a variety of options really - have a look at perlipc. But it's not as simple a matter as 'just passing a reference' unfortunately.
Use Storable to store data in first script and retrieve it from other.
firstscript.pl
store (\%hash, "/home/chankey/secondscript.$$") or die "could not store";
system("perl", "secondscript.pl", $$) == 0 or die "error";
secondscript.pl
my $parentpid = shift;
my $ref = retrieve("/home/chankey/secondscript.$parentpid") or die "couldn't retrieve";
print Dumper $ref;
You've received the %hash in $ref. Now use it the way you want.
You can't pass a reference from one script to another - that reference only has meaning within the currently running instance of perl.
You would need to "serialise" the data in the first script, and then "deserialise" it in the second.
Your way of calling perl file is wrong.
Just change the way of calling it and you are done.
Script1.pl
---------------------------------
#!/usr/bin/perl -w
use strict;
use warnings;
my %hash = (
'a' => "Harsha",
'b' => "Manager"
);
system("perl","script2.pl",%hash);
Use this %hash in another perl script as shown below.
Script2.pl
----------------------------------
#!/usr/bin/perl -w
use strict;
use warnings;
my %hash = #ARGV;
my $string = "a";
if (exists($hash{$string})){
print "$string = $hash{$string}\n";
}
OutPut is
a = Harsha