So I'm writing a relatively simple program that prompts the user for a command, add, subtract, etc, and then prompt for numbers to complete that operation. Everything is written and it compiles fine, but when I enter a command in (add, subtract, etc.) it isn't comparing it properly. Instead of entering the operation branch of the if case, it goes to the invalid command catch I added. Here is part of the code that contains the declaration and the first if statement.
my $command = <STDIN>;
my $counter = 1;
#perform the add operation if the command is add
if (($command eq 'add') || ($command eq 'a')){
my $numIn = 0;
my $currentNum = 0;
#While NONE is not entered, input numbers.
while ($numIn ne 'NONE'){
if($counter == 1){
print "\nEnter the first number: ";
}else{
print "\nEnter the next number or NONE to be finished.";
}
$numIn = <STDIN>;
$currentNum = $currentNum + $numIn;
$counter++;
}
print "\nThe answer is: #currentNum \n";
#perform the subtract operation if the command is subtract
}`
Does anyone know why if I enter in add it skips this?
$command probably still has the new line attached to it, so eq will fail. because "add" != "add\n"
You might consider just checking the first letter of your command, say with a regular expression
$command =~ /^a/i
or use chop on $command to remove the last character.
chop($command)
Related
I wanted to assign variable name separated by comma based on user input using function.
I will get the user input using below script and it will call function for variable assignment
while [ "$ans" != "q" ]
do
clear
echo "Choose your subject"
echo "Press q once done"
echo " 1.Science"
echo " 2.Maths"
echo " 3.English"
...
read ans
case $ans in
1) clear
Science;;
2) clear
Maths;;
3) clear
English;;
....
esac
done
clear
subjects=""
Science()
{
subjects+="$subjects Science"
}
Maths()
{
subjects+="$subjects Maths"
}
English()
{
subjects+="$subjects English"
}
At the end I wanted to have variable subjects to have option choose by the user:
Etc:
Science,Maths
Maths,English
Science,English
English
In bash, the function definition must be placed before any calls to the function.
The line subjects="" must be placed before the while loop. Otherwise its value will get lost (will be set to empty string) on exit from the loop.
The += operator causes double concatenation in the line subjects+="$subjects Science", since the right hand side contains already the expansion of the subjects variable. Either subjects="$subjects Science", or subjects+=" Science" must have been used (the same is also true for other lines in which the += operator is used). Besides, since a comma separated list is desired, a , character must be used while concatenating strings instead of space character. For example: subjects="$subjects,Science"
So a corrected script could be like this:
#!/bin/bash
subjects=""
Science() {
subjects="$subjects,Science"
}
Maths() {
subjects="$subjects,Maths"
}
English() {
subjects="$subjects,English"
}
while [ "$ans" != "q" ]; do
clear
echo "Choose your subject"
echo "Press q once done"
echo " 1.Science"
echo " 2.Maths"
echo " 3.English"
read -r ans
case $ans in
1) Science;;
2) Maths;;
3) English;;
esac
done
subjects=${subjects:1} # to remove the leading ',' character
echo "Your selections are $subjects"
Note: I wouldn't normally use a function just to append a simple string to a variable.
Following script, I'm using which performs everything well except it adds three extra curly brackets at the end of file.
abc.jso contains many lines where few lines have only opening/closing curly brackets (In case, this information helps)
I tried by making print $a " $var"; instead of print $a "$var";. In short I added space in front of this print & it worked.
open (my $a,'+<',"abc.jso") or die $!;
my #lines=<$a>;
seek $a,0,0;
while (#lines) {
my $var = shift #lines;
if ($var=~ /^\s*\"(netlist|filelist)\" : \".*$blk.*\",/) {
print $a " \"netlist\" : \"/t98/pnr/work/$blk"."_rk/run/dc/$blk"."_post_dft.v\",\n";
$count++;
print "Netlist got replaced\n";
}
elsif ($var=~ /^\s*\"spf\" : \".*$blk.*\"/) {
print $a " \"spf\" : \"/t98/scan/atpg/t98_1.0/spf/$blk".".scan_compress.spf\"\n";
$count++;
print "SPF got replaced\n";
}
else {
print $a "$var";
}
}
close($a);
Can someone explain, why it is happening? Is there any such corner case of text handling that I'm not aware of?
Having some sample input and output would help but let me take a guess. Try truncating the file by the seek:
truncate $a,0;
seek $a,0,0;
Without the truncate you will be overwriting what was there before. This works if you write more information than you read but not so good if you write less than you read.
I am attempting to write a code that will encrypt letters with a basic cyclic shift cipher while leaving any character that is not a letter alone. I am trying to do this through the use of a sub that finds the new value for each of the letters. When I run the code now,it formats the result so there is a single space between every encrypted letter instead of keeping the original formatting. I also cannot get the result to be only in lowercase letters.
sub encrypter {
my $letter = shift #_;
if ($letter =~ m/^[a-zA-Z]/) {
$letter =~ y/N-ZA-Mn-za-m/A-Za-z/;
return $letter;
}
else {
return lc($letter);
}
}
print "Input string to be encrypted: ";
my $input = <STDIN>;
chomp $input;
print "$input # USER INPUT\n";
my #inputArray = split (//, $input);
my $i = 0;
my #encryptedArray;
for ($i = 0; $i <= $#inputArray; $i++) {
$encryptedArray[$i] = encrypter($inputArray[$i]);
}
print "#encryptedArray # OUTPUT\n";
The problem is how you are printing the array.
Change this line:
print "#encryptedArray # OUTPUT\n";
to:
print join("", #encryptedArray) . " # OUTPUT\n";
Here is an example that illustrates the problem.
#!/usr/bin/perl
my #array = ("a","b","c","d");
print "#array # OUTPUT\n";
print join("", #array) . " # OUTPUT\n";
Output:
$ perl test.pl
a b c d # OUTPUT
abcd # OUTPUT
According to the Perl documentation on print:
The current value of $, (if any) is printed between each LIST item.
The current value of $\ (if any) is printed after the entire LIST has
been printed.
So two others ways to do it would be:
#!/usr/bin/perl
my #array = ("a","b","c","d");
$,="";
print #array, " #OUTPUT\n";
or
#!/usr/bin/perl
my #array = ("a","b","c","d");
$"="";
print #array, " #OUTPUT\n";
Here is a related answer and here is documentation explaining $" and $,.
Those spaces in your output from $" (list separator) because you use print "#encryptedArray" to print that array, which equals print join($", #encryptedArray), therefore you could disable them by
local $" = '';
or you could join that #encryptedArray by yourself before you print it, just as suggested by #Matt.
Note that there is no need for such complexity. tr/// - also known as y/// - wil convert the whole string for you. Like this
use strict;
use warnings;
print "Input string to be encrypted: ";
chomp(my $input = <STDIN>);
print "$input # USER INPUT\n";
(my $encrypted = $input) =~ tr/N-ZA-Mn-za-m/A-Za-z/;
print "$encrypted # OUTPUT\n";
I am writing a program that takes numbers from the command line until the user enters a blank line.
Should the user enter something that is neither newline nor numeric, it notifies the user, and continues.
While everything works, I have use warnings turned on, and it doesn't seem to like the second if conditional if the enters something invalid.
Argument "foo" isn't numeric in numeric eq (==) at adder.pl line 25, <STDIN> line 4.
I don't like running the program with this warning. How can I improve my code?
This is my program
#!/usr/bin/perl
use strict;
use warnings;
#declare variable
my $number = 0; #final answer
my $input;
#prompt the user
print "Input some integers, line by line. When you are done, press return to add them up." . "\n";
while (1) {
#get input from user
$input = <STDIN>;
#remove newlines
chomp($input);
#user pnches in newline
if ($input eq '') { #if the answer is new line
#quit the loop
last;
} #end of if statement
#user punches in bad input
elsif ($input == 0 && $input ne '0' && $input ne '') {
#tell the user what happened and how to rectify it
print "Input must be an integer." . "\n";
} # end of elsif statement
else {
chomp($input);
$number += $input;
} # end of else statement
} #end of while
print "Total is: $number\n";
Perl does DWIM very well. It is famous for it.
So, whatever language you have come from - it looks like C - forget about checking for both strings and numbers: a Perl scalar variable is whatever you ask it to be.
That means something like
elsif ($input == 0 && $input ne '0' && $input ne '') {
makes little sense. Anything read from the keyboard is initially a string, but it will be a number if you want. You are asking for $input to evaluate as zero but not to be the literal string 0. That applies to very few strings, for instance 00 or 0e0.
I think this is what you meant to write. Please take a look.
Isn't it clearer without comments?
use strict;
use warnings;
print "Input some integers line by line. When you are done, press return to add them up\n";
my $total = 0;
while (<>) {
chomp;
last unless /\S/;
if (/\D/) {
print "Input must be an integer\n";
next;
}
$total += $_;
}
print "Total is: $total\n";
Since Perl is untyped, and you are using $input as both a number and a string, you get that warning because "foo" isn't a number and "==" is used to compare equality of numbers.
You first need to check to see if $input is a number or not. One suggestion:
if ($input =~ /^\d+$/)
{
$number += $input;
}
else
{
print "Input must be an integer.\n";
}
According to my research the '\b' character used in perl print statements should act like a "backspace", that is, moving the cursor one character back, and deleting the current character. For this reason, I had planned to use this operation to print operational status on a single line, updating as it progressed. However, I noticed that while the cursor does indeed move back, the characters underfoot are not deleted, and therefore, longer messages remain after shorter print statements. I have compiled the following sample code to explain my findings:
#!/usr/bin/perl
use strict;
use warnings;
my $m;
#set to nonzero so that the screen will update before \n
local $| = 1;
print "Current number shown: ";
$m = "LONG MESSAGE TEMP";
print $m;
print "\b" x length($m);
foreach(1..22) {
$m = $_;
print $m;
print "\b" x length($m);
#sleep 1; #Uncomment to see updates
}
print "\n";
And this was the output:
Current number shown: 22NG MESSAGE TEMP
If this is indeed the correct operation of '\b', is there another escape that deletes the character as well as moving the cursor back? I would like to avoid using '\r' which starts at the beginning of the current line. Otherwise, how am I using the escapes incorrectly?
"\b" is just a fancy way of writing chr(0x08). Your terminal will likely move the cursor rather than display anything, but that's entirely up to it.
If you can rely on it, then you can achieve what you want by overwriting with spaces.
my $last_length = 0;
sub update {
my ($s) = #_;
print("\b" x $last_length);
print(" " x $last_length);
print("\b" x $last_length);
print($s);
$last_length = length($s);
}
Or with less flicker:
my $last_length = 0;
sub update {
my ($s) = #_;
my $diff = $last_length - length($s);
print("\b" x $last_length);
print($s);
print(" " x $diff);
print("\b" x $diff);
$last_length = length($s);
}
Just output some extra space characters to overwrite what you need to overwrite.
#!/usr/bin/env perl
use strict;
use warnings;
use Time::HiRes qw(sleep);
sub backspace {
print "\b" x $_[0];
print " " x $_[0];
print "\b" x $_[0];
}
local $| = 1;
my $m = "LONG MESSAGE TEMP";
print "Current number shown: ", $m;
sleep 1;
for (1..22) {
backspace( length($m) );
$m = $_;
print $m;
sleep 0.2;
}
print "\n";
Depending on how it is used, \b can have a special meaning within a Perl command:
\b is the backspace character only inside a character class. Outside a character class, \b alone is a word-character/non-word-character boundary.
To substitute "def" for each occurrence of the word "ABC" within a file, use the Perl command:
perl -pi -e 's/\bABC\b/def/g' file
which will leave strings such as "ZABCD" unchanged.