Perl: open/close capturing return code - perl

myb.py
import time
import sys
stime = time.time()
run_until = 600
cnt = 0
while True:
dur = time.time() - stime
if dur > run_until:
break
cnt += 1
print cnt
time.sleep(1)
if cnt == 10:
sys.exit(2) <---- capture 2
mya.pl
use FileHandle;
my $myexe = 'myb.py';
my $FH = FileHandle->new;
open $FH, q{-|},
"$myexe 2>&1"
or print "Cannot open\n";
process_output($FH);
close $FH or warn $!;
sub process_output {
my ($fh) = #_;
while (my $line = <$fh>) {
chomp $line;
print "$line\n";
}
}
OUTPUT:
1
2
3
4
5
6
7
8
9
10
Warning: something's wrong at ./mya.pl line 10.
if i change the line to:
my $err = close $FH;
it gives me a blank for $err.
Question: How can I capture the return code 2 from myb.py in mya.pl?

As documented in http://perldoc.perl.org/functions/close.html, the exit value is available as part of $?. But it can be more convenient to use a wrapper:
use IPC::System::Simple qw(capture $EXITVAL EXIT_ANY);
my #output = capture([0,2], "$myexe 2>&1");
print #output;
print "Program exited with value $EXITVAL\n";
The [0,2] says that exit values 0 or 2 are expected, and anything else is a fatal error; you can use EXIT_ANY instead.
This does get all the output at the end, rather than when it is produced, though.

When open creates a child, close functions as waitpid and sets $? accordingly.
$ perl -e'
open(my $fh, "-|", #ARGV)
or die $!;
print while <$fh>;
close($fh);
if ($? == -1 ) { die $!; }
elsif ($? & 0x7F) { die "Killed by signal ".($? & 0x7F)."\n"; }
elsif ($? >> 8 ) { die "Exited with error ".($? >> 8)."\n"; }
' perl -E'
$| = 1;
for (1..5) {
say;
sleep 1;
}
exit 2;
'
1
2
3
4
5
Exited with error 2

Related

Perl - match begin / end string in a file

How do I write a PERL for the below:
I would like to read in a file.
Then match between GAME and END from the file read in. I want the line that has "GOOD CATCH 1 5 15 5 15" to become "GOOD CATCH 1 10 30 10 30". The final number is multiply by 2 or 3 or 4...etc.
Print out the same file but with the changed above.
Input file
***********
GAME
BALL X1 ;
GOOD CATCH 1 5 15 5 15 ;
END
Output file
***********
GAME
BALL X1 ;
GOOD CATCH 1 10 30 10 30 ;
END
# I am not sure how to match $GOOD CATCH line and multiply the last 4 digit. Is there such a thing as LINDEX or SCAN in perl?
open (infile, "<", "inputfile.txt") or die "could not open:$!";
open (outfile, ">", "outputfile.txt") or die "could not open:$!";
while (<infile>)
if (/GAME/) {
if (/BALL X1/) {
$GOOD CATCH =
}
}
elseif (/END) {
print ;
}
close (inffie);
close (outfile);
I would recommend having a look on a parser like Regexp::Grammars. Anyway, here is a simpler approach using regex:
use strict;
use warnings;
my $fn = "file.txt";
open ( my $fh, '<', $fn ) or die "Could not open file '$fn': $!";
my $str = do { local $/; <$fh> };
close $fh;
$str =~ s/^(GAME.*?^END)/multiply($1)/msge;
print $str;
sub multiply {
my ( $str ) = #_;
$str =~ s/(GOOD CATCH.*?;)/_multiply($1)/ge;
return $str;
}
sub _multiply {
my ( $str ) = #_;
$str =~ s/(\d+)/_mul_number($1)/ge;
return $str;
}
sub _mul_number {
my ($num) = #_;
return $num * 2;
}
Output:
GAME
BALL X1 ;
GOOD CATCH 2 10 30 10 30 ;
END

Using pointers to lines in a file in perl

I'm trying to use some sort of pointers in perl so that I can look at two at two files that are sorted in alphabetical order and match things in both the files if they have the same name in the first column. The way i'm searching through each file though is I'm looking at which lines first column is lower in alphabetical order and then moving the pointer on that file to the next line. Somewhat similar to the pointers in merge sort. The code below is an example of what I want.
Using these two files.
set1
apple 17 20
boombox 23 29
carl 25 29
cat 22 33
dog 27 44
set2
ants yes
boombox no
carl yes
dentist yes
dice no
dog no
I can make a script that does something like this
($name, $affirmation) = first line in set2; #part I'm confused about I just kind of need some sort of command of something that will do this
while (<>){
#set1 = split;
while ($name < set1[0]){
($name, $affirmation) = next line in set2; # part i'm confused about I just kind of need some sort of command of something that will do this
}
if ($name = $set[0]{
print #set1, $affirmation;
}
This is how I would run it
./script.txt set1
I would end up with
boombox 23 29 no
carl 25 29 yes
dog 27 44 no
.
.
Edit:
I tried some code in some of the answers to see if I could make some functional code out of it but I seem to be running into problems, and some of the syntax in the answers I could not understand so I'm having a lot of trouble figuring out how to debug or solve this.
This is my specific example using the folllowing two text files
text.txt
Apples 0 -1 -1 0 0 0 0 -1
Apricots 0 1 1 0 0 0 0 1
Fruit 0 -1 -1 0 0 0 0 -1
Grapes 0 -2 -1 0 0 0 0 -2
Oranges 0 1 1 0 0 0 0 -1
Peaches 0 -2 -1 0 0 0 0 -2
text2.txt
Apples CHR1 + 1167628 1170420 1 1 N
Apricots CHR1 - 2115898 2144159 1 1 N
Oranges CHR1 - 19665266 19812066 1 1 N
Noidberry CHR1 - 1337728 1329993 1 1 N
Peaches CHR1 - 1337275 1342693 1 1 N
And this script
script.pl
#!/usr/bin/perl
use warnings;
my $file_1 = $ARGV[0];
my $file_2 = $ARGV[1];
open my $fh1, '<', $file_1 or die "Can't open $file_1: $!";
open my $fh2, '<', $file_2 or die "Can't open $file_2: $!";
open(my $single, '>', 'text.txt');
open(my $deep, '>', 'text2.txt');
OUTER: while (my $outer = <$fh1>){
chomp $outer;
#CopyNumber = split(' ', $outer);
($title, $title2) = split('\|', $CopyNumber[0]);
#print 'title: ',$title,' title2: ',$title2,"\n";
my $numLoss = 0;
my $deepLoss = 0;
for ($i = 1; $i <= $#CopyNumber; $i++){
#print "$CopyNumber[$i], $#CopyNumber, $i, \n";
if ($CopyNumber[$i] < 0){
$numLoss = $numLoss + 1;
if ($CopyNumber[$i] <-1){
$deepLoss = $deepLoss + 1;
}
}
}
if ($GeneSym and (($GeneSym cmp $title)==0)){ #or (($GeneSym cmp $title2)==0))){
print $single $Chrom,"\t",$Start,"\t",$Stop,"\t",$numLoss/$#CopyNumber,"\n";
print $deep $Chrom,"\t",$Start,"\t",$Stop,"\t",$deepLoss/$#CopyNumber,"\n";
next OUTER;
}
INNER: while (my $inner = <$fh2>){
($GeneSym, $Chrom, $Strand, $Start, $Stop, $MapId, $TotalMap, $AbnormalMerge, $Overlap) = split(' ', $inner);
$Chrom =~ s/CHR/hs/ee;
my $cmp = ($GeneSym cmp $title);
next OUTER if $cmp < 0;
if ($cmp==0){ #or (($GeneSym cmp $title2)==0)){
print $single $Chrom,"\t",$Start,"\t",$Stop,"\t",$numLoss/$#CopyNumber,"\n";
print $deep $Chrom,"\t",$Start,"\t",$Stop,"\t",$deepLoss/$#CopyNumber,"\n";
next OUTER;
}
}
}
If I run ./script.pl text.txt text2.txt I should get this printed into Number.txt
//corresponding to columns 2,4,5 of text2.txt and the last column being the percentage of columns which have a number lower than 0
hs1 1167628 1170420 0.375 //For Apples
hs1 2115898 2144159 0 //For Apricots
hs1 19665266 19812066 0.125 //For Oranges
hs1 1337275 1342693 0.375 //For Peaches
Instead I get this
hs1 1167628 1170420 0.375
hs1 2115898 2144159 0
hs1 1337275 1342693 0.375
So I'm just getting an error where
hs1 19665266 19812066 0.125 //For Oranges
isn't printing
Quite like you state, with: use cmp for comparison, split line into two terms.
For each line of FILE1 file go through lines of FILE2 file, exiting when a match is found. Once the FILE2 overshoots alphabetically move to the next line of FILE1.
use warnings 'all';
use strict;
sub process {
my ($name, $affirm_1, $affirm_2) = #_;
print "$name $affirm_1 $affirm_2\n";
}
my $file_1 = 'set1.txt';
my $file_2 = 'set2.txt';
open my $fh1, '<', $file_1 or die "Can't open $file_1: $!";
open my $fh2, '<', $file_2 or die "Can't open $file_2: $!";
my ($name_2, $affirm_2);
FILE1: while (my $line1 = <$fh1>) {
chomp $line1;
my ($name_1, $affirm_1) = split ' ', $line1, 2;
if ($name_2) {
my $cmp = $name_1 cmp $name_2;
next FILE1 if $cmp < 0;
if ($cmp == 0) {
process($name_1, $affirm_1, $affirm_2);
next FILE1;
}
}
FILE2: while (my $line2 = <$fh2>) {
chomp $line2;
($name_2, $affirm_2) = split ' ', $line2, 2;
my $cmp = $name_1 cmp $name_2;
next FILE1 if $cmp < 0;
if ($cmp == 0) {
process($name_1, $affirm_1, $affirm_2);
next FILE1;
}
}
}
Comments on a few remaining details.
Once a FILE2 line "overshoots," in the next iteration of FILE1 we need to first check that line, before entering the FILE2 loop to iterate over its remaining lines. For the first FILE1 line the $name_2 is still undef thus if ($name_2).
Updated for edited post.
use warnings 'all';
use strict;
sub process_line {
my ($single, $deep, $rline, $GeneSym, $Chrom, $Start, $Stop) = #_;
my ($numLoss, $deepLoss) = calc_loss($rline);
$Chrom =~ s/CHR/hs/;
print $single (join "\t", $Chrom, $Start, $Stop, $numLoss/$#$rline), "\n";
print $deep (join "\t", $Chrom, $Start, $Stop, $deepLoss/$#$rline), "\n";
}
sub calc_loss {
my ($rline) = #_;
my ($numLoss, $deepLoss) = (0, 0);
for my $i (1.. $#$rline) {
$numLoss += 1 if $rline->[$i] < 0;
$deepLoss += 1 if $rline->[$i] < -1;
}
return $numLoss, $deepLoss;
}
my ($Number, $NumberDeep) = ('Number.txt', 'NumberDeep.txt');
open my $single, '>', $Number or die "Can't open $Number: $!";
open my $deep, '>', $NumberDeep or die "Can't open $NumberDeep: $!";
my ($file_1, $file_2) = ('set1_new.txt', 'set2_new.txt');
open my $fh1, '<', $file_1 or die "Can't open $file_1: $!";
open my $fh2, '<', $file_2 or die "Can't open $file_2: $!";
my ($GeneSym, $Chrom, $Strand, $Start, $Stop,
$MapId, $TotalMap, $AbnormalMerge, $Overlap);
FILE1: while (my $line1 = <$fh1>) {
next if $line1 =~ /^\s*$/;
chomp $line1;
my #line = split ' ', $line1;
if ($GeneSym) {
my $cmp = $line[0] cmp $GeneSym;
next FILE1 if $cmp < 0;
if ($cmp == 0) {
process_line($single, $deep, \#line,
$GeneSym, $Chrom, $Start, $Stop);
next FILE1;
}
}
FILE2: while (<$fh2>) {
next if /^\s*$/;
chomp;
($GeneSym, $Chrom, $Strand, $Start, $Stop,
$MapId, $TotalMap, $AbnormalMerge, $Overlap) = split;
my $cmp = $line[0] cmp $GeneSym;
next FILE1 if $cmp < 0;
if ($cmp == 0) {
process_line($single, $deep, \#line,
$GeneSym, $Chrom, $Start, $Stop);
next FILE1;
}
}
}
This produces the desired output with given sample files. Some shortcuts are taken, please let me know if comments would be helpful. Here are a few
Much error checking should be added around.
I assume the first field of FILE1 to be used as it stands. Otherwise changes are needed.
Processing is split into two functions, calculations being separate. This is not necessary.
$#$rline is the index of the last element of $rline arrayref. If this is too much syntax to stomach use #$rline - 1, for example as (0..#$rline-1)
Some comments on the code posted in the question:
Always, always, please use warnings; (and use strict;)
loop over indices is best written foreach my $i (0..$#array)
The regex modifier /ee is very involved. There is absolutely no need for it here.
You're right. It's exactly like a merge sort, except only matching lines are output.
sub read_and_parse1 {
my ($fh) = #_;
defined( my $line = <$fh> )
or return undef;
my ($id, #copy) = split(' ', $line); # Use split(/\t/, $line) if tab-separated data
my ($gene_sym) = split(/\|/, $id);
return [ $gene_sym, #copy ];
}
sub read_and_parse2 {
my ($fh) = #_;
defined( my $line = <$fh> )
or return undef;
return [ split(' ', $line) ]; # Use split(/\t/, $line) if tab-separated data
}
my $fields1 = read_and_parse1($fh1);
my $fields2 = read_and_parse2($fh2);
while ($fields1 && $fields2) {
my $cmp = $fields1->[0] cmp $fields2->[0];
if ($cmp < 0) { $fields1 = read_and_parse1($fh1); }
elsif ($cmp > 0) { $fields2 = read_and_parse2($fh2); }
else {
my ($gene_sym, #copy) = #$fields1;
my (undef, $chrom, $strand, $start, $stop, $map_id, $total_map, $abnormal_merge, $overlap) = #$fields2;
$chrom =~ s/^CHR/hs/;
my $num_loss = grep { $_ < 0 } #copy;
my $deep_loss = grep { $_ < -1 } #copy;
print($single_fh join("\t", $chrom, $start, $stop, $num_loss/#copy ) . "\n");
print($deep_fh join("\t", $chrom, $start, $stop, $deep_loss/#copy ) . "\n");
$fields1 = read_and_parse1($fh1);
$fields2 = read_and_parse2($fh2);
}
}
Output:
$ cat single.txt
hs1 1167628 1170420 0.375
hs1 2115898 2144159 0
hs1 19665266 19812066 0.125
hs1 1337275 1342693 0.375
$ cat deep.txt
hs1 1167628 1170420 0
hs1 2115898 2144159 0
hs1 19665266 19812066 0
hs1 1337275 1342693 0.25

An alternative to block eval?

Is there an equivalent of the following -
eval { die "reason 1"; }; warn $# if $#;
eval { die "reason 2"; }; warn $# if $#;
eval { die "reason 3"; }; warn $# if $#;
.
.
As you can notice, the following code wont print out every possible reasons for the script to die..
eval {
die "reason 1";
die "reason 2";
die "reason 3";
};
warn $# if $#;
[EDIT] I would like to know all possible reasons the script (that uses lot many libraries) can fail. The die statements are not in a place i can edit.
overriding die is not exactly an alternative to eval, but this is what I think you're asking about, trapping die and turning it into a warn, log die and resume/continue program
$ perl -e " eval{die 1;}; die 2; die 3; "
2 at -e line 1.
$ perl -Mwarnerous -e " eval{die 1;}; die 2; die 3; "
FAKE die : 2 at -e line 1
FAKE die : 3 at -e line 1
$ cat warnerous.pm
*CORE::GLOBAL::die = sub {
unless( $^S ){
warn( qq{FAKE die : #_ #{[sprintf q{at %s line %s },(caller)[1,2] ]}\n} );
}
};
1;
Do you mean something like this?
my $problems;
for my $r (1 .. 3) {
eval { die "reason $r"; 1 } or $problems .= $#;
}
warn "There were the following problems:\n$problems";

Perl: how to prevent SIGALRM from closing a pipe?

My pipe (filehandle, socket) breaks (sometimes). I can reproduce it with the following code:
my $counter = 5;
alarm(1);
open(FH,"while(sleep 2); do date; done |") or die $!;
while (<FH>) { print; }
close(FH);
BEGIN {
$SIG{ALRM} = sub {
print "alarm!\n";
exit if --$counter == 0;
alarm(1);
};
}
Which will produce:
alarm!
alarm!
Thu Feb 7 11:46:29 EST 2013
alarm!
alarm!
alarm!
If I strace this process, I see that the spawned shell gets a SIGPIPE. However, the Perl process happily continues. How do I fix this?
The problem is that <FH> is returning false because of an interrupted system call. I am not sure if this is the idiomatic way to handle this in perl (and would love to see a better answer), but the following seems to work:
my $counter = 5;
alarm 1;
open my $fh, '-|', 'while(sleep 2); do date; done' or die $!;
loop:
while (<$fh>) { print; }
goto loop if $!{EINTR};
close $fh;
BEGIN {
$SIG{ALRM} = sub {
print "alarm!\n";
alarm 1;
exit if --$counter <= 0;
};
}

How can I run a program (with a timeout) and know how it ended (signal, exit code, etc) in Perl on a modern Linux?

I need to run a program, feed some strings on its stdin, read its stdout/stderr and know how it ended. I have to know if it received a signal (segfault, etc) and its exit code. Also, if the program runs for more than some amount of time, I have to know it (and kill it).
How would you do that? Is there a module that handles this kind of things?
Re. exit codes and signals, see here. In particular:
Exit codes in the range 129-255 represent jobs terminated by Unix
"signals". Each type of signal has a number, and what's reported as
the job exit code is the signal number plus 128. Signals can arise
from within the process itself (as for SEGV, see below) or be sent to
the process by some external agent (such as the batch control system,
or your using the "bkill" command).
By way of example, then, exit code 64 means that the job deliberately
terminated its execution by calling "exit(64)", exit code 137 means
that the job received a signal 9, and exit code 140 represents signal
12.
Ok here's what I came up with: (usage example at the end)
#!/usr/bin/perl
use strict;
use warnings;
use IPC::Open3;
use Symbol 'gensym';
use Time::HiRes 'time';
use POSIX ':sys_wait_h';
use IO::Select;
sub run_prog {
my ($progin, $timeout, $cmd, #args) = #_;
my ($progres, $progout, $progerr);
my ($fdin, $fdout, $fderr);
my $outsel = IO::Select->new();
my $errsel = IO::Select->new();
$fderr = gensym;
my $pid = open3($fdin, $fdout, $fderr, $cmd, #args);
my $start = time;
syswrite $fdin, $progin;
close $fdin;
$outsel->add($fdout);
$errsel->add($fderr);
$progout = '';
$progerr = '';
my $last_activity = $start;
my $select_timeout = 0.1;
my $ret;
while(time - $last_activity < $timeout) {
if($outsel->can_read($select_timeout)) {
my $buf;
$ret = sysread($fdout, $buf, 1000);
if(!defined $ret) {
warn "out ndef";
last;
}
$progout .= $buf;
$last_activity = time;
}
if($errsel->can_read($select_timeout)) {
my $buf;
$ret = sysread($fderr, $buf, 1000);
if(!defined $ret) {
warn "err ndef";
last;
}
$progerr .= $buf;
$last_activity = time;
}
$ret = waitpid($pid, WNOHANG);
# still exists, continue
if($ret == 0) {
next;
}
# process exited/signaled
elsif($ret > 0) {
$progres = $?;
last;
}
# process doesn't exists??
else {
die "wat";
}
}
close $fdout;
close $fderr;
# timeout
if(time - $last_activity >= $timeout) {
kill 9, $pid;
waitpid($pid, 0);
$progres = $?;
}
return ($progres, $progout, $progerr);
}
my #r = run_prog("qsdjkqsodj\nqsdqsd\n", 0.9, './bbb', $ARGV[0] || 0);
printf "out: <%s>\nerr: <%s>\n", $r[1], $r[2];
my $x = $r[0];
if ($x == -1) {
print "failed to execute: $!\n";
}
elsif ($x & 127) {
printf "child died with signal %d, %s coredump\n",
($x & 127), ($x & 128) ? 'with' : 'without';
}
else {
printf "child exited with value %d\n", $x >> 8;
}