I am having a logic issue with my UNIX number guessing game. The menu system works just fine (1 for play, 2 to quit, defensive response otherwise). But once in the game I am getting an output as follows:
OPTIONS:
(1. Play)
(2. Exit)
Enter number 1 or 2.
1
Please enter you're guess.
5
pgm01[43]: [5: not found [No such file or directory]
pgm01[47]: [5: not found [No such file or directory]
Yay!!! 5 is the right number!!!
Your total number of tries were 0.
Here is the code (FYI this is in the KSH shell). I am guessing I have some sort of syntax error somewhere...
#!/bin/ksh
# CS2351 - UNIX Programming
# Program 1
x=$RANDOM%100+1
tries=0
guessInt=0
userOption=0
while ((1==1))
do
print "Welcome to the UNIX Number Guessing Game!"
print "A random number between 1 and 20 has been selected."
print "===========\nDirections: \n==========="
print "1. Enter your guess. The program will tell you if it is high or low."
print "2. The program will tell you if it is high or low."
print "3. Change your guess."
print "4. The computer will tell you how many guesses you had."
print "=======\nOPTIONS\n======="
print "1: Play \n2: Exit"
print "Enter number 1 or 2."
"Enter number 1 or 2."
read userinput
case "$userinput" in
1)
break;;
2)
print "Quitting program!!!"
exit;;
*)
print "The input $userinput is invalid."
print "Returning to main menu..."
print "\n\n\n";;
esac
done
print "Please enter you're guess."
read guessInt
while (($guessInt != "q"));
do
if [$guessInt -lt $x];
then
print "Your guess is too low, try again! (q to quit)."
tries=tries+1
elif [$guessInt -gt $x];
then
print "Your guess is too high, try again! (q to quit)."
tries=tries+1
else
print "Yay!!! $guessInt is the right number!!!"
break
fi
done
print "Your total number of tries were $tries."
exit
Any insight into this issue is appreciated!
As far as I can tell, you need double brackets for conditionals and some whitespace is significant. You need something like:
if [[ $guessInt -lt $x ]]; then
tries should also start at 1, since there's no way you can win without guessing first.
You should also read guessInt again inside the while loop if their guess is wrong, because if you guess wrong the first time, it will just repeat forever (since the value of $guessInt doesn't change between iterations of the loop).
Take a look at these shell script syntax examples; it's got a lot of bash stuff but there's some decent coverage of ksh as well.
Related
I have perl script that I am running in Windows. In the script I call another perl scrip. I am trying get both of those scripts to print to the cmd window and a file. This basically how I am doing the call
using IO::Tee
open (my $file, '>>', "C:\\Logs\\logfile.txt") or die "couldn't open log file: $!";
me $tee = IO::Tee->new(\*STDOUT, $file);
# doing some stuff
print $tee "log about what i just did";
# do more stuff
print $tee "more logs";
print $tee `c:\\secondScript.pl arg1`;
print $tee "done with script";
The second script is basically
# do stuff
print "script 2 log about stuff";
# do more stuff
print "script 2 log about more stuff";
print "script 2 done";
This does get everything to the screen and a file. However, I don't see the "script 2 log about stuff", "script 2 log about more stuff", and "script 2 done" until after script 2 has finished. I would like to see all of that stream to the screen and the file as soon as the print is reached.
Printing to STDOUT is usually line buffered (to speed things up) when output goes to a terminal and block buffered otherwise (e.g. when redirecting output to a file).
You can think of it as if anything printed is first placed into a buffer (typically 4096 bytes large) and only when the buffer is full (i.e. 4096 characters were printed), it gets output to the screen.
line buffered means the output is only shown on screen after a \n is found (or the buffer is exhausted). You don't have \ns in your 2nd script, so no output is shown until either a) \n comes, b) buffer is full, or c) the script ends.
block buffered means the output is shown only when the buffer is full. \ns don't influence this here (except for counting as one character).
To avoid buffering there's a magic variable called $|. From the docs (scroll down to the $| section):
If set to nonzero, forces a flush right away and after every write or
print on the currently selected output channel.
So you could append "\n" to your print statements or – better – set $| = 1; on top of your 2nd script (only once, not for each print). This will slow down the output of the 2nd script (in theory) but for a few lines it will make no difference.
I have a perl script that now works from the windows command line (ActivePerl installed). It sends commands to turn ports on/off on a power controller (smart power strip). I have it setup so that I take a couple of command line arguments. The IP address of the device, and the power operation I want to take on that device. i.e. 10.0.40.1 on should turn the device with the IP 10.0.40.1 on, or rather turn the appropriate port on in the power controller.
I have an if elseif statement that will evaluate the IP address passed to the script and determine which power controller and what port is appropriate. The only other operation is the port and the operation are joined so if the device is on port 3 of the controller and the operation passed was 'on' then the new variable becomes 3on. This is what the remainder of the script understands and being a novice, it was easier to leave that unchanged.
THE ISSUE: When I run this command from windows either by
perl Lpower.pl 10.0.30.15 on
or
Lpower.pl 10.0.30.15 on
The issue is the point of this was to allow another application to invoke the script on my client. The other app is a piece of network management software. Through supported custom GUI extension I was able to add items to the menu for each device being managed. I have one for "Power On" and another for "Power Off". I point it to the Lpower script with the path local to my client machine, and it is invoked and runs, it also passes the arguments as expected, but my if statement does not work. It will not match on any IP, nor will it match on the operation "on" "off". I don't need to evaluate the latter, I just put a statement in their to test.
My script prints the variables, and they do show up, but the if statement doesn't work. It does not appear that it is adding leading whitespace or anything.
Here is a code snip of the relevant parts:
# $language = "PerlScript"
# $interface = "1.0"
#!c:\perl64\bin\perl -w -CA
...
print STDERR "UserUtil $version\n\n";
my ($ipaddr, $onoff) = #ARGV;
print "$ipaddr $onoff\n";
if ($onoff eq "off") {print "it matched off"}
if ($ipaddr eq "10.0.40.1") {
$epc='10.0.30.92';
$port='1';
}
elsif ($ipaddr eq "10.0.40.105") {
$epc='10.0.30.92';
$port='2';
}
elsif (($ipaddr eq "10.0.40.100") || ($ipaddr eq "10.0.40.101")) {
$epc='10.0.30.92';
$port='3';
}
...
else {print "no matches found"}
$oper="$port$onoff";
$base='http://'.$auth.'#'.$epc.'/';
print "$epc $oper $ipaddr";
cmd($oper) && die "Unknown Command $_\n";
There are some print statements in there for debugging. The output from CLI looks like this (and it works, ports power on/off as expected):
10.0.30.15 off
it matched off10.0.30.93 1off 10.0.30.15
Invoked from the web-app (which launches the script on my client)
10.0.30.15 on
Unknown Command
no matches found 10.0.30.15 off
Why would it be different? Anything I can do to force the correct handling of the arguments passed? If I read correctly they are expected to be UTF-8 encoded strings, could that app be sending something different and therefore the if statements break?
One more note. The app calls the script directly and passes the arguments, it does not call the perl executable, my environment knows what to do with a pl script. Not sure if that makes a difference.
Any Help is much appreciated. I do have some coding in my distant back-ground, but never for a profession, and completely new to perl.
The relevant part of your script
my ($ipaddr, $onoff) = #ARGV;
print "$ipaddr $onoff\n";
if ($ipaddr eq "10.0.40.1") { print "matched\n"; }
else {print "no matches found\n"}
run it as
perl power.pl 10.0.40.1 off
prints:
10.0.40.1 off
matched
run it as
perl power.pl "10.0.40.1 off" #two words as one arg
prints
10.0.40.1 off
no matches found
when you add
use strict;
use warnings;
my ($ipaddr, $onoff) = #ARGV;
print "$ipaddr $onoff\n";
if ($ipaddr eq "10.0.40.1") { print "matched\n"; }
else {print "no matches found\n"}
will print:
Use of uninitialized value $onoff in concatenation (.) or string at power.pl line 5.
10.0.40.1 off
no matches found
I'm new to PERL but have picked it up rather quickly as I work in C.
My question seems simple, but for the life of me I cant find a simple answer. Basically I want to print something on the same line as user input
Example
print "Please Enter Here: ";
my $input = <STDIN>;
chomp $input;
print " - You Entered: $input";
Output
Please Enter Here: 123
- You Entered: 123
This is undesired as I want all of this on one line in the terminal window. At the moment it prints the second string on the line below once the user has pressed the enter key. I guess what i'm going to need to do is something with STDIN like ignore the carriage return or newline but I'm not sure.
Desired Output
Please Enter Here: 123 - You Entered: 123
I don't know why this seems to be a complicated thing to google but I just haven't fathomed it, so any help would be appreciated.
Ta
Well, this is interesting...
First, you'd have to turn off terminal echoing, using something like IO::Stty. Once you do that, you could use getc. Note that the getc perldoc page has a sample program that could be used. You can loop until you get a \n character as input.
You can also try Term::ReadKey.
I have never done this myself. I'll have to give it a try, and if I succeed, I'll post the answer.
The Program
Ended up I already had Term::ReadKey installed:
#! /usr/bin/env perl
#
use warnings;
use strict;
use feature qw(say);
use Term::ReadKey;
ReadMode 5; # Super Raw mode
my $string;
print "Please Enter Here: ";
while ( my $char = getc ) {
last if ord( $char ) < 32; # \r? \n? \l?
$string .= $char;
print "$char";
}
ReadMode 0;
say qq( - You entered "$string".);
I realized that, depending how the terminal is setup, it's hard to know exactly what is returned when you press <RETURN>. Therefore, I punted and look for any ASCII character that's before a space.
One more thing: I didn't handle what happens if the user hit a backspace, or other special characters. Instead, I simply punt which may not be what you want to do.
In the end I did:
"\033[1A\033[45C
It uses the code functions for line up and 45 indent and it works. Thanks for all the help though.
I know I can add watches for variables as well as expressions with the w command, is it possible to tell it to simply parse each command and stop if a certain piece of text is in that command?
Something like w m/bad command/ where $_ is a string containing the next command that is being executed.
I'm working with Komodo on Windows doing remote CGI debugging right now, but I can also use perl -d on a linux shell.
Update:
I'll try to make this clearer. There's a file with data on disk being deleted, and I don't know where. The program I'm debugging consists of a lot of .pl files being required into each other, and the complete codebase is even larger. I could of course grep through this for either the part of the data files' name (I know the extension), or for unlink. But since I don't know which code files are being used by my program, that does not get me very far.
Let's consider this piece of code, which is going to be debugged:
if ($foo == 1) {
unlink 'filename.example';
}
Now when I'm debugging this, I'd like the debugger to stop if the next perl command (from the script) which is about to be executed contains /example/. I want kind of a hook that is run before the execution of the next command.
That way, I don't need to know where (as in which line or file) the thing I'd like to break at is, nor do I need a variable name (as there is none).
I know this is weird to explain, but I thought it might be a common problem. Maybe there's another approach altogether?
Watch points are set with the 'w' command:
BD<1> w $cannonballs
will break the program every time $cannonballs changes.
BD<1> w $i == 19
will break when the value at $i is set to 19. For example
$ cat foo.pl
foreach $i ( 0..100 ) {
print "$i\n";
}
$ perl -d foo.pl
Loading DB routines from perl5db.pl version 1.28 Editor support available.
Enter h or `h h' for help, or `man perldebug' for more help.
main::(foo.pl:2): foreach $i ( 0..100 ) {
DB<1> w $i == 3
DB<2> c
0
1
2
Watchpoint 0: $i == 3 changed:
old value: ''
new value: '1'
main::(foo.pl:3): print "$i\n";
DB<2> p $i
3
DB<3> q
Watchpoint 0: $i == 3 changed:
old value: '1'
new value: ''
DB<3> q
$
You'll probably want to use the full namespace of the variable, otherwise you'll get breaks any time any variable with the same name come into or goes out of scope.
I am unaware of a module that does exactly what you want, but Devel::Trace is a very small module (20 lines of code) that prints every line before it is executed.
You could create a subclass of that, and modify the DB::DB subroutine to set $DB::single = 1 if the code matches your desired string.
See brian d foy's post about Devel::Trace for a more thorough description, with examples.
The debugger already can do that (well, almost). It is not called a watch expression, though, it is called a breakpoint.
You can set a breakpoint to a line and condition:
b 33 /pattern/i
You have to specify the line number, though. See Perl debugging.
You can set a break point in code using
$DB::single = 1;
To use a break point in a watch configuration, enclose the break in a conditional that matches what you want to watch.
In your case:
if ( $command =~ /example/ ) {
$DB::single = 1;
}
should serve.
Check out Tie::Trace, which can alert you of the locations that a variable's value is changed. I suppose you could even hack it with $DB::single=1 expressions to break in the debugger every time a variable's value changes.
I am running into a strange issue in perl that I can't seem to find an answer for.
I have a small script that will parse data from an external sorce (be it file, website, etc). Once the data has been parsed, it will then save it to a CSV file. However, the issue is when I am writing the file or printing to screen the data, it seems to be truncating the beginning of the string. I am using strict and warnings and I am not seeing any errors.
Here is an example:
print "Name: " . $name . "\n";
print "Type: " . $type. "\n";
print "Price: " . $price . "\n";
print "Count: " . $count . "\n";
It will return the following:
John
Blue
7.99
5
If I attempt to do it this way:
print "$name,$type,$price,$count\n";
I get the following as a result:
,7.99,5
I tried the following to see where the issue begins and get the following:
print "$name\n";
print "$name,$type\n";
print "$name,$type,$price\n";
print "$name,$type,$price,$count\n";
Results:
John
John,Blue
,7.99
,7.99,5
I am still learning perl, but can't seem to find out (maybe due to lack of knowledge) of what is causing this. I tried debugging the script, but I did not see any special character in the price variable that would cause this.
The string in $price ends with a Carriage Return. This is causing your terminal to move the cursor to the start of the line, causing the first two fields to be overwritten by the ones that follow.
You are probably reading a Windows text file on a unix box. Convert the file (using dos2unix, for example), or use s/\s+\z//; instead of chomp;.
If the CR made into the middle of a string, you could use s/\r//g;.
Per #Mat suggestion I ran the output through hexdump -C and found there was a carriage return (indicated by the hex value 0d). Using the code $price =~ s/\r//g; to remove the CR from the line of text fixed the problem.
Also, the input file was in Windows format not Unix, ran the command dos2unix to fix that.