I have a Perl script called master.pl. I have a 2D array in that called #inputarray.
I need to pass the 2D array values from master.pl to another program called child.pl and access the data in the child.pl.
I have tried a lot but I'm not able to dereference the array in child.pl.
Can you please help me?
master.pl
system "start perl child.pl $appl $count #inputarray";
child.pl
($appl, $count, #inputarray) = #ARGV;
for (my $k = 0; $k < $count + 1; $k++) {
for (my $m = 0; $m < 6; $m++) {
print "$inputarray[$k][$m] ";
}
print "\n";
}
Method 1:
Have a look at the standard module Data::Dumper, it's ideal for what you want.
Save your data structure in a temporary file using Data::Dumper and then read it in your second script.
Method 2:
Using Storable to store array in first script and retrieve it from other.
Edit (after you provided your code):
See you can access the array like this
master.pl
#!/usr/local/bin/perl
use strict;
use warnings;
use Storable;
my #inputarray = ([1, 2, 3], [4, 5, 6], [7, 8, 9]);
store (\#inputarray, "/home/chankey/child.$$") or die "could not store";
system("perl", "child.pl", $$) == 0 or die "error";
child.pl
#/usr/local/bin/perl
use strict;
use warnings;
use Storable;
use Data::Dumper;
my $parentpid = shift;
my $ref = retrieve("/home/chankey/child.$parentpid") or die "coudn't retrieve";
print Dumper $ref;
print $$ref[0][0]; #prints 1
Output
$VAR1 = [
[
1,
2,
3
],
[
4,
5,
6
],
[
7,
8,
9
]
]; #from Dumper
1 #from print $$ref[0][0]
As you can see from dump, you've received the #inputarray in $ref. Now use it the way you want.
Check Storable for any kind of perl data serialization/deserialization.
For passing data between processes on POSIX systems I use named pipes, for better compatibility with Windows you could use temporary files, using File::Temp.
You could use an anonymous pipe, which works in the same way on UNIX and Windows (I assume you are using Windows because of the start.
Try this:
use strict;
use warnings;
my $appl = 'orange';
my #inputarray = ([0,1,2],[3,4,5],[6,7,8]);
We don't need $count, you can get the number of elements in the array using scalar context, or the highest index number using $#inputarray;
I have omitted the start because it makes it difficult to debug (the console window closes after the run).
my $cmd = 'perl child.pl';
open(my $pipe, '|-', $cmd) or
die "Unable to execte $cmd; $!";
Using Data::Dumper we can add eval statements and reduce whitespace generation:
use Data::Dumper;
local $Data::Dumper::Purity = 1;
local $Data::Dumper::Indent = 0;
my $dat = Data::Dumper->new([\$appl,\#inputarray],
[qw($appl $inputarray)]);
print $pipe $dat->Dump();
close ($pipe);
Now for the child, which reads the pipe (input stream):
use strict;
use warnings;
my ($inp) = <STDIN>;
my ($appl, $inputarray);
eval "$inp";
print "appl = $$appl\n";
Use of eval is usually frowned upon, and it can give a security hole, so use with care. I think it is justifed here.
Your loops are a tad complex, and smell of C. These are rather more Perlish:
for my $ref (#$inputarray) {
for my $ele (#$ref) {
print "$ele "
}
print "\n"
}
YAML is safer, because it does not require the eval, but needs to be installed.
Related
%hash = ('abc' => 123, 'def' => [4,5,6]);
how can I store above hash in file using data dumper in Perl
Files can only contain sequences of bytes, so you need to convert the data structure into a sequence of bytes somehow. This process is called serialization.
The possibilities available to you are endless, but a few are worth mentioning:
JSON is a very common choice.
YAML is more flexible.
Storable is specifically made for Perl data structures.
There is also Data::Dumper, as you say.
use Data::Dumper qw( );
sub serialize {
my ($x) = #_;
local $Data::Dumper::Purity = 1; # Required for some data structures.
local $Data::Dumper::Useqq = 1; # Optional. Limits output to ASCII.
local $Data::Dumper::Sortkeys = 1; # Optional. Makes revision control easier.
return Data::Dumper->Dump([$x], ["x"]);
}
print($fh serialize($x));
Data::Dumper isn't a particularly good choice, since there's no existing module to safely deserialize the structure in Perl[1], and there's even less support outside of Perl.
sub deserialize {
my ($s) = #_;
my $x;
eval($s); # XXX Unsafe!
die($#) if $#;
return $x;
}
If you're ok with limiting yourself to data structure JSON can serialize (by setting Purity to 0), then you could use Data::Undump to safely deserialize. But then why not just use JSON?!
use Data::Dumper
open (FL, ">", "file.txt") or die "Cant open file $! ";
print FL Dumper \%hash;
close FL;
"how can I store above hash in file using data dumper in Perl"
Store it as JSON so it can be read back by (almost) anything, using Data::Dumper configured to print JSON.
use strict;
use warnings;
use Data::Dumper;
local $Data::Dumper::Pair = ' : ';
local $Data::Dumper::Quotekeys = 1;
local $Data::Dumper::Useqq = 1;
local $Data::Dumper::Terse = 1;
my %hash = ('abc' => 123, 'def' => [4,5,6]);
open my $file, '>', 'foo.json' or die $!;
print $file Dumper \%hash;
Output:
$ cat foo.json
{
"def" : [
4,
5,
6
],
"abc" : 123
}
(Note: I would of course rather use a dedicated JSON-handling module for this, but you asked ....)
I want to (ab-)use the global %ENV to store a hash. This seems to work differently for %ENV than for ordinary hashes. in the program below, the $ENV{myhash} still contains 'myhash' => 'HASH(0x7ffad8807380)' and the %ahash is still around. is it possible to convert the hex address back to point at its location, instead of just containing the string? I guess I could serialize and unserialize the hash instead. what is the right way to do this?
#!/usr/bin/perl -w
use common::sense;
use Data::Dumper;
my %QENV = ( nohash => 'noh' );
my %ahash= ( hv1 => 'htext1', hv2 => 'htext2' );
$QENV{'myhash'} = \%ahash;
print "works: ". Dumper(\%QENV)."\n\n\n";
$ENV{'myhash'} = \%ahash;
print "fails: ". Dumper(\%ENV)."\n";
%ENV is a magical hash. It reflects the process's environment. Reading from it reads from the environment, and writing to it changes the environment.
If you can guarantee the referenced variable is still alive (by it still being in scope or by it having its REFCNT increased), you can indeed create a reference to it from the address.
use strict;
use warnings;
use Data::Dumper;
use Inline C => <<'__EOS__';
SV* _newRV(IV iv) {
return newRV((SV*)iv);
}
__EOS__
my %hash = ( a => 1, b => 2 );
my $ref = \%hash;
my $ref_string = "$ref";
my $addr = hex( ( $ref_string =~ /\((.*)\)/ )[0] );
my $ref2 = _newRV($addr);
print(Dumper($ref2));
I have no idea why you'd want to do this. It would not permit another process to access the data since one process can't access the memory of another.
You seem to want to share data. Here's an example I put out there often that shows how to store data in a JSON file, then retrieve it. JSON is cross-language, so the data can be used by many programming languages, not just Perl. Although this example is within a single script, imagine it being two different Perl applications:
use warnings;
use strict;
use JSON;
my $file = 'data.json';
my %h = (
foo => 'bar',
raz => {
ABC => 123,
XYZ => [4, 5, 6],
}
);
my $json = encode_json \%h;
# write the JSON to a file, and close it
open my $fh, '>', $file or die $!;
print $fh $json;
close $fh or die $!;
# open the JSON file, and read it
open my $json_file, '<', $file or die $!;
my $read_json;
{
local $/;
$read_json = <$json_file>;
}
my $perl_hash = decode_json $read_json;
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
I have written a hash structure into a file using
print FILE Data::Dumper->Dump([$x], [ qw(*x) ]);
How do I read this back from file? If I use eval as shown in the following snippet, all I get is $VAR1 = undef;
local $/; $hash_ref = eval <FILE>;
You can use the core module Storable to do this type of task.
use Storable;
store \%table, 'file';
$hashref = retrieve('file');
If those two statements are in the same file, then you can't do it that way unless you close the file handle and reopen the same file for reading. You could do something fancy like opening it with mode +> and then using seek to get back to the beginning before you read again, but why bother, especially since you already have a variable in the same program that contains the same data.
So I assume you are dumping the data from one program and reading it again in another. The problem with using Data::Dumper->Dump([$x], ['*x']) is that it will dereference $x and invent a variable of the appropriate type with the given name. So if $x is a hash reference it will name the variable %x, if it is an array reference then it will be #x etc.
It is far better to remove the star and just write Data::Dumper->Dump([$x], ['x']), which will avoid the dereferencing and name the variable $x.
To read the file back in you should just use do. Like this
use strict;
use warnings;
use Data::Dumper;
my $x = {
a => 1,
b => 2,
};
open my $fh, '>', 'dumper.txt' or die $!;
print $fh Data::Dumper->Dump([$x], ['x']);
close $fh;
my $data = do 'dumper.txt';
If you are constrained to using the form of Data::Dumper call that you show, then you must provide a variable of the appropriate type, like this
use strict;
use warnings;
use Data::Dumper;
my $x = {
a => 1,
b => 2,
};
open my $fh, '>', 'dumper.txt' or die $!;
print $fh Data::Dumper->Dump([$x], ['*x']);
close $fh;
my %data = do 'dumper.txt';
Note that, although the Data::Dumper output file refers to a variable %x, the file is run as a separate Perl program and there is no %x in the program that executes the do.
I tried several ways to export an existing hash. The only way I found that works is to create a new var that is a pointer to the existing hash.
Then Borodin's answer works well.
use strict;
use warnings;
use Data::Dumper;
my %x = (
a => 1,
b => 2,
);
my $x = \%x; # <<< Added so $x is a reference to %x.
open my $fh, '>', 'dumper.txt' or die $!;
print $fh Data::Dumper->Dump([$x], ['*x']);
close $fh;
my %data = do 'dumper.txt';
I've got a small programme that basically processes lists of blast hits, and checks to see if there's overlap between the blast results by iterating blast results (as hash key) through hashes containing each blast list.
This involves processing each blast input file as $ARGV in the same way. Depending on what I'm trying to achieve, I might want to compare 2, 3 or 4 blast lists for gene overlap. I want to know how I can write the basic processing block as a subroutine that I can iterate over for however many $ARGV arguments exist.
For example, the below works fine if I input 2 blast lists:
#!/usr/bin/perl -w
use strict;
use File::Slurp;
use Data::Dumper;
$Data::Dumper::Sortkeys = 1;
if ($#ARGV != 1){
die "Usage: intersect.pl <de gene list 1><de gene list 2>\n"
}
my $input1 = $ARGV[0];
open my $blast1, '<', $input1 or die $!;
my $results1 = 0;
my (#blast1ID, #blast1_info, #split);
while (<$blast1>) {
chomp;
#split = split('\t');
push #blast1_info, $split[0];
push #blast1ID, $split[2];
$results1++;
}
print "$results1 blast hits in $input1\n";
my %blast1;
push #{$blast1{$blast1ID[$_]} }, [ $blast1_info[$_] ] for 0 .. $#blast1ID;
#print Dumper (\%blast1);
my $input2 = $ARGV[1];
open my $blast2, '<', $input2 or die $!;
my $results2 = 0;
my (#blast2ID, #blast2_info);
while (<$blast2>) {
chomp;
#split = split('\t');
push #blast2_info, $split[0];
push #blast2ID, $split[2];
$results2++;
}
my %blast2;
push #{$blast2{$blast2ID[$_]} }, [ $blast2_info[$_] ] for 0 .. $#blast2ID;
#print Dumper (\%blast2);
print "$results2 blast hits in $input2\n";
But I would like to be able to adjust it to cater for 3 or 4 blast lists inputs. I imagine a sub routine would work best for this, that is invoked for each input, and might look something like this:
sub process {
my $input$i = $ARGV[$i-1];
open my $blast$i, '<', $input[$i] or die $!;
my $results$i = 0;
my (#blast$iID, #blast$i_info, #split);
while (<$blast$i>) {
chomp;
#split = split('\t');
push #blast$i_info, $split[0];
push #blast$iID, $split[2];
$results$i++;
}
print "$results$i blast hits in $input$i\n";
print Dumper (\#blast$i_info);
print Dumper (\#blast$iID);
# Call sub 'process for every ARGV...
&process for 0 .. $#ARGV;
UPDATE:
I've removed the hash part for the last snippet.
The resultant data structure should be:
4 blast hits in <$input$i>
$VAR1 = [
'TCONS_00001332(XLOC_000827),_4.60257:9.53943,_Change:1.05146,_p:0.03605,_q:0.998852',
'TCONS_00001348(XLOC_000833),_0.569771:6.50403,_Change:3.51288,_p:0.0331,_q:0.998852',
'TCONS_00001355(XLOC_000837),_10.8634:24.3785,_Change:1.16613,_p:0.001,_q:0.998852',
'TCONS_00002204(XLOC_001374),_0.316322:5.32111,_Change:4.07226,_p:0.00485,_q:0.998852',
];
$VAR1 = [
'gi|50418055|gb|BC078036.1|_Xenopus_laevis_cDNA_clone_MGC:82763_IMAGE:5156829,_complete_cds',
'gi|283799550|emb|FN550108.1|_Xenopus_(Silurana)_tropicalis_mRNA_for_alpha-2,3-sialyltransferase_ST3Gal_V_(st3gal5_gene)',
'gi|147903202|ref|NM_001097651.1|_Xenopus_laevis_forkhead_box_I4,_gene_1_(foxi4.1),_mRNA',
'gi|2598062|emb|AJ001730.1|_Xenopus_laevis_mRNA_for_Xsox17-alpha_protein',
];
And the input:
TCONS_00001332(XLOC_000827),_4.60257:9.53943,_Change:1.05146,_p:0.03605,_q:0.998852 0.0 gi|50418055|gb|BC078036.1|_Xenopus_laevis_cDNA_clone_MGC:82763_IMAGE:5156829,_complete_cds
TCONS_00001348(XLOC_000833),_0.569771:6.50403,_Change:3.51288,_p:0.0331,_q:0.998852 0.0 gi|283799550|emb|FN550108.1|_Xenopus_(Silurana)_tropicalis_mRNA_for_alpha-2,3-sialyltransferase_ST3Gal_V_(st3gal5_gene)
TCONS_00001355(XLOC_000837),_10.8634:24.3785,_Change:1.16613,_p:0.001,_q:0.998852 0.0 gi|147903202|ref|NM_001097651.1|_Xenopus_laevis_forkhead_box_I4,_gene_1_(foxi4.1),_mRNA
TCONS_00002204(XLOC_001374),_0.316322:5.32111,_Change:4.07226,_p:0.00485,_q:0.998852 0.0 gi|2598062|emb|AJ001730.1|_Xenopus_laevis_mRNA_for_Xsox17-alpha_protein
You can't inject a variable value in the middle of a variable name. (Well, you can but you shouldn't. Even then you and can't use array indexing in the middle of the name.)
These names aren't valid:
#blast[$i]_info
#blast[$i]_ID
You need to move the index to the end:
#blast_info[$i]
#blast_ID[$i]
That said, I'd get rid of the arrays completely and use a hash instead.
Your second code snippet doesn't show a call to your subroutine. Unless it's explicitly called it will never run and your program will do nothing. I'd modify the process sub to take a single argument and call it for each element of #ARGV. e.g.
process($_) foreach #ARGV;
Here's how I'd write your program:
use strict;
use warnings;
use Data::Dumper;
my #blast;
push #blast, process($_) foreach #ARGV;
print Dumper(\#blast);
sub process {
my $file = shift;
open my $fh, '<', $file or die "Can't read file '$file' [$!]\n";
my %data;
while (<$fh>) {
chomp;
my ($id, undef, $info) = split '\t';
$data{$id} = $info;
}
return \%data;
}
It isn't quite clear what your resulting data structure should look like. (I took my best guess.) I recommend reading perlreftut to gain a better basic understanding of references and using them to build data structures in Perl.