I am encountering an issue while trying to execute a perl script that takes 2 number arguments, well say $ARGV[0] is 2, and $ARGV[1] is 4. I need to print a list that shows 2,3,4 with no comma after the last item. Below is my script as it is now:
unless ((#ARGV)==2){
print "error: incorrect number of arguments",
"\n",
"usage: inlist.pl a b (where a < b)",
"\n";
exit VALUE;
}
if ($ARGV[0] > $ARGV[1]){
print "error: first argument must be less than second argument",
"\n",
"usage: intlist.pl a b (where a < b)",
"\n";
exit VALUE;
}
else {
$COUNTER=$ARGV[0];
while($COUNTER <= $ARGV[1]){
print $COUNTER;
$COUNTER += 1;
if ($COUNTERELATIONAL < $ARGV[1]){
print ", ";
}
else {
print "\n";
}
$COUNTERSYNTAX
}
}
exit VALUE;
I tried using join but to no avail I keep getting as return of 2,3,4,
I feel like i must be missing something simple
Rewriting your code to simplify it:
# Prefer 'if' over 'unless' in most circumstances.
if (#ARGV != 2) {
# Consider using 'die' instead of 'print' and 'exit'.
print "error: incorrect number of arguments\n",
"usage: inlist.pl a b (where a < b)\n";
# Not sure what VALUE is, but I assume you've
# defined it somewhere.
exit VALUE;
}
if ($ARGV[0] > $ARGV[1]) {
# Consider using 'die' instead of 'print' and 'exit'.
print "error: first argument must be less than second argument\n",
"usage: intlist.pl a b (where a < b)\n";
exit VALUE;
}
# Removed 'else' branch as it's unnecessary.
# Use 'join' instead of a complicated loop.
print join(',', $ARGV[0] .. $ARGV[1]), "\n";
# This looks like a successful execution to me, so
# that should probably be 'exit 0'.
exit VALUE;
If I was writing it for myself, I'd make it a little shorter:
my %errors = (
NUMBER => 'incorrect number of arguments',
RANGE => 'first argument must be less than second argument',
);
my $usage = 'usage: inlist.pl a b (where a < b)';
die "$errors{NUMBER}\n$usage\n" if #ARGV != 2;
die "$errors{RANGE}\n$usage\n" if $ARGV[0] > $ARGV[1];
print join(',', $ARGV[0] .. $ARGV[1]), "\n";
exit 0;
I figured it out:
while($COUNTER <= $ARGV[1]){
print $COUNTER;
$COUNTER += 1;
if ($COUNTER <= $ARGV[1]){
print ", ";
}
else {
print "\n";
}
I needed to change the if to $COUNTER and <= and it printed correctly. Thank you for the join suggestion, that would have worked if I had designed the script more efficiently
Related
I am trying to create a simple perl assignment for equality check and goto label concept.
User enters numbers , equality check happens, user is asked if they want to check more , if yes then it repeats, else it exits. Using "goto " for this
Issue- y/n checks for repeating,y is for repeating Label. even if I enter "n" it keeps going to the label Loop .
Why is the "if" condition containing "goto" not getting honored?
Code below
#Checking Equality
Loop: print "\Checking Equality\n";
print "Enter number for variable a\n";
$a = <stdin>;
print "Enter number for variable b\n";
$b = <stdin>;
if ( $a == $b ) {
print 'a and b are equal';
print "\n\n";
}
else {
print 'a and b are not equal';
}
print "\n\n";
print "do you want to check more? Enter y/n\n";
$c = <stdin>;
if ( $c == "y" ) {
goto Loop;
}
elsif ( $c == "n" ) {
print "Exiting\n";
}
Output-
Checking Equality
Enter number for variable a
3
Enter number for variable b
4
a and b are not equal
do you want to check more? Enter y/n
n
Checking Equality #### despite of entering n goto Loop is getting executed
Enter number for variable a
Just use chomp function to remove newline(s), and use eq comparison for string.
use strict;
use warnings;
Loop: print "\nChecking Equality\n";
print "Enter number for variable a\n";
my $a = <stdin>;
print "Enter number for variable b\n";
my $b = <stdin>;
if ( $a == $b ) {
print 'a and b are equal';
print "\n\n";
}
else {
print 'a and b are not equal';
}
print "\n\n";
print "do you want to check more? Enter y/n\n";
chomp(my $c = <stdin>);
if ( $c eq "y" ) {
goto Loop;
}
else {
print "Exiting\n";
}
You have chosen not best approach to use a label for goto.
Instead loop would be more appropriate to perform user input. User stays in the loop until he/she will not specify that he/she ready to leave.
User's input also should be stripped off newline symbol \n before it can be used for comparison.
As user suppose to provide input several time then it would be beneficial to use small subroutine to print 'input prompt', collect input, strip newline symbol and return input value. By doing so the program becomes shorter and easier to read.
String comparison operation performed with eq, number comparison performed with ==.
use strict;
use warnings;
use feature 'say';
my $answer = 'y';
while( $answer eq 'y' ) {
my $num_1 = get_input('Enter variable num_1: ');
my $num_2 = get_input('Enter variable num_2: ');
my $compare = $num_1 == $num_2 ? 'EQUIAL' : 'NOT EQUIAL';
say "\nNumbers are $compare\n";
$answer = get_input('Would you like to continue? (y/n) ');
$answer = lc $answer;
say '-' x 35;
}
sub get_input {
my $msg = shift;
my $input;
print $msg;
$input = <>;
chomp $input;
return $input;
}
I'm trying to read a file that looks like this:
> SOME HEADER
ABCDEFGHIJKLMNOP
QRSTUPWXYZ123456
I need to be able to read the characters A,B,C,D etc..per character, so I'm using this loop:
while (read $file, my $char, 1){
print $char;
print $.;
print "\n\n";
}
But the problem is, I need to skip the "SOME HEADER". I need to skip it not on the basis of it being the first line, but on the basis of having a "> SOME HEADER" substring.
printing the $. always outputs "6" even though it's not the correct line number.
open my $file,"<","file.txt";
<$file>; #Removing first line
while (read $file, my $char, 1)
{
print $char;
}
Or set the flag and check it
my $flag = 0;
while (read $file, my $char, 1)
{
$flag = 1 and next if(($char =~m/\n|\r\n/) && ($flag == 0));
next if $flag == 0;
print $char;
}
Wouldn't it be easier to process line-by-line and then character-by-character?
use strict;
use warnings;
while (<DATA>) {
chomp;
next if /^> SOME HEADER/;
for my $char (split(//)) {
print "$char $.\n";
}
}
__DATA__
> SOME HEADER
ABCDEFGHIJKLMNOP
QRSTUPWXYZ123456
Output:
A 2
B 2
C 2
...
4 3
5 3
6 3
I was having a difficulty about perl script..I have a script that includes a loop but never breaks out of it once it gets the exit status zero.The loop will run only once the exit status results a 1 upon checking initially by the "if" statement.
my $a = "/home/vivek/generated_mdsum_reference.out";
my $b = "/home/vivek/generated_mdsum_new.out";
sub CHECK {
print "\n";
print "\n";
print "\n\tGenerating MD5SUM ....";
my $dumpfile = "/home/vivek/file_dump.dmp";
print "\n";
# my $md5sum = system("md5sum $dumpfile");
my $md5sum = `md5sum $dumpfile`;
print "\n";
print "\nChecksum: $md5sum.";
# Put checksum in file
my $ochksumfile = "/home/vivek/generated_mdsum_new.out";
open (my $fh, '>', "$ochksumfile") or die "no file:$!";
my $output = $md5sum;
die "$!" if $?;
$value = (split / /, "$output")[0];
print $fh $value;
my $status =compare($b, $a);
}
my $status =compare($b, $a);
if ( $status == 1 ){
do
CHECK;
until ($status == 0 ) {
print "\n\tfiles are now Ok. Exiting..";
print "\n\t";
}
All the variables I have set in there works fine, I only ended up in the until loop which it keeps on running endlessly which I think it cannot get through until the rest of the function "CHECK"
Please help me guys.
Let's look at the block in question
my $status = compare($b, $a);
if ( $status == 1 ) {
do
CHECK;
until ($status == 0 ) {
print "\n\tfiles are now Ok. Exiting..";
print "\n\t";
}
whether you meant it or not, this is equivalent to
my $status = compare($b, $a);
if ( $status == 1 ) {
do CHECK;
until ( $status == 0 ) {
print "\n\tfiles are now Ok. Exiting..";
print "\n\t";
}
So the contents of your until loop is just two print statements, which aren't going to change the value of $status so it will loop forever
What I think you meant is
my $status = compare($b, $a);
if ( $status == 1 ) {
do {
CHECK;
} until $status == 0;
print "\n\tfiles are now Ok. Exiting..";
print "\n\t";
}
which will repeatedly call CHECK until $status is set to zero
EXCEPT THAT you should always use lower-case characters in your local identifiers. Upper-case is reserved for global identifiers such as package names. In this situation you have unwittingly created a CHECK block
perldoc perlmod says this
Five specially named code blocks are executed at the beginning and at the end of a running Perl program. These are the BEGIN , UNITCHECK , CHECK , INIT , and END blocks.
These code blocks can be prefixed with sub to give the appearance of a subroutine (although this is not considered good style). One should note that these code blocks don't really exist as named subroutines (despite their appearance). The thing that gives this away is the fact that you can have more than one of these code blocks in a program, and they will get all executed at the appropriate moment. So you can't execute any of these code blocks by name.
So because "these code blocks don't really exist as named subroutines" your program will only call CHECK during compilation, and subsequent explicit calls will be ignored
So rename your subroutine to check and chnage your code to this
my $status = compare($b, $a);
if ( $status == 1 ) {
do {
CHECK;
} until $status == 0;
print "\n\tfiles are now Ok. Exiting..";
print "\n\t";
}
and all will be well
I do not understand why the until loop should stop.
In short, you get an md5 sum for a file in your "CHECK" sub.
sub CHECK {
open (my $fh, '>', "/home/vivek/generated_mdsum_new.out");
my $dump_file = "/home/vivek/file_dump.dmp";
my $md5sum = `md5sum $dump_file`;
my $value = (split(" ",$md5sum))[0];
print $fh $value;
close $fh;
compare($a,$b);
# question: what is in $a / $b and what does compare do ?
}
Now you compare $a and $b and return that result.
Assuming $a / $b contain something useful, there is nothing in the until block or CHECK block that I see to change either, so the comparison would keep returning the same result ( assuming "compare" compares and does not change anything, which would be a bad naming if it did ).
So in the until block, do something to influence the CHECK, otherwise you are stuck in a loop.
You should always use lower-case characters in your local identifiers. Upper-case is reserved for global identifiers such as package names. In this situation you have unwittingly created a CHECK block
perldoc perlmod says this
Five specially named code blocks are executed at the beginning and at the end of a running Perl program. These are the BEGIN , UNITCHECK , CHECK , INIT , and END blocks.
These code blocks can be prefixed with sub to give the appearance of a subroutine (although this is not considered good style). One should note that these code blocks don't really exist as named subroutines (despite their appearance). The thing that gives this away is the fact that you can have more than one of these code blocks in a program, and they will get all executed at the appropriate moment. So you can't execute any of these code blocks by name.
So because "these code blocks don't really exist as named subroutines" your program will only call CHECK during compilation. That means explicit calls to CHECK will be ignored, and $status never changes
Change your subroutine to check and all will be well
The problem is here:
until ($status == 0 ) {
print "\n\tfiles are now Ok. Exiting..";
print "\n\t";
}
Nowhere in the loop are you changing the value of $status.
The "do CHECK;" isn't part of the loop.
Would eval a file by the name of the return value of the CHECK sub
If it had actually called that sub.
Which it won't because it isn't actually a subroutine.
The other form of until is:
do {
CHECK; # doesn't work as this is a special name
} until $status == 0;
print "\n\tfiles are now Ok. Exiting..";
print "\n\t";
Which is still a problem as CHECK is a special name for a block that only gets called once at CHECK time, that you can't actually call.
Plus the code in CHECK would always have the same result, so calling it repeatedly doesn't make sense, and could still result in an infinite loop if it didn't work the first time.
This is how I might have written it
This is a first pass of making your code make sense to me, also fixing some of the errors pointed out above.
I also changed $a and $b to $ref and $new since $a and $b are reserved variables.
I improved it by using modules that come with Perl, so that I don't have to check the return values of open() and close() ( autodie ), or rely on conventions of a particular platform ( Digest::MD5, and File::Spec::Functions ).
I assume that you loaded File::Compare.
I removed the setting of $status from the check sub to reduce the use of global variables.
#! /usr/bin/env perl
use strict;
use warnings;
use 5.10.1; # set minimum version which was released in 2009
use autodie;
use File::Spec::Functions qw' catfile catdir rootdir curdir ';
use Digest::MD5;
use File::Compare qw' compare ';
# should be // not ||, but it will work if your dir isn't named "0" or ""
my $basedir = $ENV{HOME} || catdir rootdir, qw' home vivek ';
# try the current directory if it doesn't exist
$basedir = curdir unless -d $basedir;
my $ref = catfile $basedir, 'generated_mdsum_reference.out';
my $new = catfile $basedir, 'generated_mdsum_new.out';
my $dumpfile = catfile $basedir, 'file_dump.dmp';
# forward declare so that we can put them at the end
sub md5_hex_file;
sub md5_sum;
sub check;
#-------------------------------------------------------
if ( compare($new, $ref) != 0 ){
if ( check($dumpfile,$new) == 0 ){
print "\n\tfiles are now Ok. Exiting..\n";
} else {
local $| = 1; # make sure the output is flushed to STDOUT
print "\n\tfiles are NOT OK. Exiting..\n";
exit 1;
}
}
#-------------------------------------------------------
# helper subroutines
sub md5_hex_file {
my ($filename) = #_;
# let Digest::MD5 read the file for us
my $ctx = Digest::MD5->new;
{ # limit scope of $fh
open my $fh, '<', $filename;
binmode $fh;
$ctx->addfile( $fh );
close $fh;
}
$ctx->hexdigest;
}
# no longer necessary
sub md5_sum {
my ($filename) = #_;
# `md5sum -b $filename`
md5_hex_file($filename) . " $filename\n";
}
sub check {
my ( $infile, $outfile ) = #_
print "\n" x 3, "\tGenerating MD5SUM ....\n";
my $md5_hex = md5_hex_file $infile;
print "\n" x 2, "Checksum: $md5_hex.\n";
# Put checksum in file
{
open my $out_fh, '>', $outfile;
print {$out_fh} $md5_hex;
close $out_fh;
}
my $status = compare $new, $ref;
return $status if $status == 0;
# add a newline and hope that fixes it
{
open my $out_fh, '>>', $outfile;
print {$out_fh} "\n";
close $out_fh;
}
return compare $new, $ref;
}
Really I think you could have just used these "one-liners"
$ perl -Mautodie -MDigest::MD5 -e \
'open $fh, q[<], shift;
print Digest::MD5->new->addfile($fh)->hexdigest' \
file_dump.dmp > generated_mdsum_new.out
$ perl -MFile::Compare -e \
'if ( compare(shift(),shift()) == 0 ){
print qq[They match\n]
} else {
print qq[They don\'t match\n]
}' \
generated_mdsum_new.out generated_mdsum_reference.out
This is really frustrating me. The script I'm writing is indexing coordinates in a hash and then using those index numbers to pull out values from an array.
The weird thing is that if the value begins with 2 or 22 it will not print. Any other number works. I'll show you two variations and output of the script.
First variation. This is what I want the script to do. Print chromosome, position, value.
#!/usr/bin/perl
use strict;
use warnings;
use File::Find;
use Scalar::Util qw(looks_like_number);
open IN, "/home/big/scratch/affy_map.txt" or die "Cannot open reference\n";
my %ref;
my $head = <IN>;
my $index = 0;
while(<IN>){
chomp $_;
my #row = split /\t/, $_;
my $value = join "\t", $row[1],$row[2];
if($row[1] == 2 && $row[2] <= 50000 && $row[2] <= 51113178) { $ref{$index}=$value; print $index."\t".$value."\n";}
if($row[1] == 22 && $row[2] <= 16300001 && $row[2] <= 20500000) { $ref{$index}=$value; print $index."\t".$value."\n"; }
$index++;
}
close(IN);
my #files;
my $masterDirect = "/nfs/archive02/big/Norm/norm_gcc/";
find(\&file_names, $masterDirect);
sub file_names {
if( -f && $File::Find::name=~/\.nzd$/)
{
push #files, $File::Find::name;
}
}
my $count=0;
foreach(#files){
$count++;
if($count % 100 == 0 ){ print "\n","-" x 10, " $count ", "-" x 10,"\n";}
undef my #probes;
open IN, $_;
#file name handling
my #inDir = split "\/", $_;
my $id = pop(#inDir);
$id =~ s/\.gcc.nzd$//;
#header test
$head =<IN>;
if(looks_like_number($head)) { push #probes, $head; }
#open output
open OUT, ">/home/big/scratch/phase1_affy/".$id."_select_probeset.txt";
#load probe array
#probes = <IN>;
close(IN);
foreach my $key (sort keys %ref){
#intended function
print OUT $ref{$key}."\t".$probes[$key];
#testing
my #temp = split "\t", $ref{$key};
foreach(#temp){if($temp[0] == 2){print $key."\t".$ref{$key}."\t".$probes[$key];}}
}
close(OUT);
}
Here's the output for the test. The printing from the reference file is flawless. The first number is the $key or index number. The second is frome $probes[$key] why is the $ref{$key} missing?
146529 0.777314368326637
146529 0.777314368326637
146530 0.116241153901913
146530 0.116241153901913
146531 0.940593233609167
146531 0.940593233609167
Variation 2.
...
foreach my $key (sort keys %ref){
print OUT $ref{$key}."\t".$probes[$key];
my #temp = split "\t", $ref{$key};
foreach(#temp){if($temp[0] == 2){print $key."\t".$ref{$key}."\n";}}
}
And its output. See now it's printing correctly. $key and $ref{$key}
146542 2 31852
146542 2 31852
146543 2 37693
146543 2 37693
146544 2 40415
146544 2 40415
146545 2 40814
I thought it might be a DOS->UNIX file problem but I performed perl -pi -e 's/\R/\n/g' input_files.txt for all the input the script sees. It prints the same value twice because there are two elements in the #temp array. I'm really at a loss right now.
Here is a hint for possible issue. In the beginning part,
if($row[1] == 2 && $row[2] <= 50000 && $row[2] <= 51113178) { $ref{$index}=$value; print $index."\t".$value."\n";}
Note that you used two "<=" for $row[2], which looks peculiar. The next line has such "problem" too. Please double check it first otherwise you may have filtered them out in the first place.
I've got my program keymap (it is not yet actually mapping any keys yet and is currently only printing out what it sees in hex) here:
#!/usr/bin/env perl
use strict;
use warnings;
use Term::ReadKey;
ReadMode 4;
END {
ReadMode 0; # Reset tty mode before exiting
}
if ($ARGV[0] ~~ ["h", "-h", "--help", "help"]) {
print "Usage: (h|-h|--help|help)|(code_in codes_out [code_in codes_out]+)\nNote: output codes can be arbitrary length";
exit;
}
$#ARGV % 2 or die "Even number of args required.\n";
$#ARGV >= 0 or warn "No args provided. Output should be identical to input.\n";
my $interactive = -t STDIN;
my %mapping = #ARGV;
{
local $| = 1;
my $key;
while (ord(($key = ReadKey(0))) != 0) {
printf("saw \\x%02X\n",ord($key));
if ($interactive and ord($key) == 4) {
last;
}
}
}
Here's what happens:
slu#new-host:~/util 20:50:20
❯ keymap a b
saw \x61
saw \x62
saw \x04
There I had typed on my keyboard abCtrl+D.
slu#new-host:~/util 20:50:24
❯ echo "^D^Da" | keymap
No args provided. Output should be identical to input.
saw \x04
saw \x04
saw \x61
saw \x0A
Use of uninitialized value $key in ord at /Users/slu/util/keymap line 30.
I'm wondering what the meaning of this is. Is it simply a case of Perl "not counting" the loop condition as "setting" $key? Is there some sort of thing I can do to suppress the warning here? I know about no warnings "uninitialized";, I don't want that.
There's a known bug that warnings issued by the condition expression of a while loop can be misattributed to the statement in the loop evaluated just before the while condition.
The code issuing the warning is actually the condition of the while loop, ord(($key = ReadKey(0))) != 0.
ReadKey(0) is returning undef, and you are trying to get the ord or it.
while (1) {
my $key = ReadKey(0);
last if !defined($key) || ord($key) == 0;
printf("saw \\x%02X\n",ord($key));
last if $interactive and ord($key) == 4;
}