Using switch statement with 5 cases within a for loop. How do I save case statementss to a database? - switch-statement

I am passing several strings ($cu1, $cu2, $us1, $us2,etc.)to another page where I use a switch statement to provide standard strings ( a, b, c, d, e ) for saving to a database (cards) and table (results)
I'm not sure how/where to enter the "$sql=" code for saving the data. It seems to be redundant to enter the code for each case statement. I've thought about using an include statement for each statement, and have also thought about using the db code after the switch statement but within the for loop. What would be the best practice and code?
for($i=1; $i<14; $i++) {
switch($i) {
case '1': $a=$cu1; $b=$us1; $c=$draw1; $d= $yn1; $e= $acct_id1; echo "$e _ $a _ $b _ $c _ $d <br>"; break;
case '2': $a=$cu2; $b=$us2; $c=$draw2; $d= $yn2; $e= $acct_id2; echo "$e _ $a _ $b _ $c _ $d <br>"; break;
} // close switch
} // close for

Related

Assign variable separated by comma based on user input using function

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.

shell/ksh syntax for a condition inside inline input

Completely forgot how to do it correctly, and probably forgot the correct terms as well.
Long inline input (I think this is what it's called), using the Teradata bteq as an example client reading the stdin (same as Oracle sql*plus or Sybase isql etc) - so this could be pretty much anything.
bteq <<!
select sql_column1
`if [ "$mode" == "mode1" ]; then`
, sql_column2
`fi`
, sql_column3
from table1
;
!
Here if the mode is "mode1" - I output 3 sql_columns, otherwise two. Now, imagine this is a very very large input, so these conditional manipulations can be very handy.
I'm pretty sure I have done this before, but covid-19 completely flushed my memory.
With this syntax I'm getting: syntax error at line xx: `then' unmatched.
How do I do this right, and what are the correct Unix terms for what I called here as a) inline input; and b) inline condition?
If your desire is to end up with a multi-line SQL statement, then you don't need a "here document" in this specific case because strings can span lines. You can do it easily like this, so long as your text doesn't have embedded quotes.
if [ "$mode" == "mode1" ]; then
col2="
, sql_column2"
else
col2=""
fi
stm="select sql_column1$col2
, sql_column3
from table1
;"
echo "stm='$stm'"
If you don't need a multi-line SQL statement, then the code is simpler.
You can solve your problem on a lot of ways. How can someone read your code after mixing bteq, SQL, Bash and special tests? I think the next approach might help:
I started with replacing bteq by cat for testing.
You can replace your test with
[[ "${mode}" == "mode1" ]] && echo ", sql_column2"
Using this in your here document (using $() and not backtics) results in
cat << END
select sql_column1
$( [[ "${mode}" == "mode1" ]] && echo ", sql_column2")
, sql_column3
from table1
;
EOF
This is not much better for the readers eyes. Now what? Make a function!
mode1_column() {
[[ "${mode}" == "mode1" ]] && echo ", sql_column2"
}
cat << EOF
select sql_column1
$(mode1_column)
, sql_column3
from table1
;
EOF
Looks like either Jeff's revision, or this one, if the "here document" and multiple conditional inclusions are too long to want to break the logic:
bteq <<!
select sql_column1
`if [ "$mode" == "mode1" ]; then
echo \"
, sql_column2
\"
fi`
, sql_column3
from table1
;
!

parsing conditional operators as strings and using as conditional operators

my $line ="Corner:Default,Output:fall_delay_slew_1,Mean=34.97p,Std- dev=1.767p,Min=30.02p,Max=39.71p"; #added semicolon
my $my_value="COND = Mean > 3"; #this has come from the parsed file.
$my_value =~ m/(\w+)\s*(.)\s*(\d+)/;
my $cond=$1;
my $sign=$2;
my $value=$3;
print "DEBUG:cond is $cond and sign $sign and value $value \n";
if ( $line =~ m/$cond=(.*?),/) {
if ( "$value $sign $1" ) {
print "$value is $sign than $1\n";
} else {
print "actual value is less\n";
}
}
If you see in the above if statement always evaluates to true.
How can I solve this kind of problem i.e $sign = "<" (could be any operator)
but when I want to compare it with $value I want it to function as an
operator and not as a string.
What you're willing to do (executing a string as code) can be done with
eval. That doesn't mean it is the most appropriate way of doing
it though. Do it only if you guarantee your input safety and check for
it.
A better way would be checking the operator your self and determining
how to proceed. If you use a recent Perl version, the given-when
feature can be handy to do this:
use feature 'switch'; # not needed if you already use 5.010 or greater
given ($sign) {
when ('<') { say "$cond less than $value" }
when ('>') { say "$cond greater than $value" }
default {
warn "unrecognized operator `$sign'\n";
# decide what to do
}
}

Get rid of warning in perl number adder code

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";
}

How to tell apart numeric scalars and string scalars in Perl?

Perl usually converts numeric to string values and vice versa transparently. Yet there must be something which allows e.g. Data::Dumper to discriminate between both, as in this example:
use Data::Dumper;
print Dumper('1', 1);
# output:
$VAR1 = '1';
$VAR2 = 1;
Is there a Perl function which allows me to discriminate in a similar way whether a scalar's value is stored as number or as string?
A scalar has a number of different fields. When using Perl 5.8 or higher, Data::Dumper inspects if there's anything in the IV (integer value) field. Specifically, it uses something similar to the following:
use B qw( svref_2object SVf_IOK );
sub create_data_dumper_literal {
my ($x) = #_; # This copying is important as it "resolves" magic.
return "undef" if !defined($x);
my $sv = svref_2object(\$x);
my $iok = $sv->FLAGS & SVf_IOK;
return "$x" if $iok;
$x =~ s/(['\\])/\\$1/g;
return "'$x'";
}
Checks:
Signed integer (IV): ($sv->FLAGS & SVf_IOK) && !($sv->FLAGS & SVf_IVisUV)
Unsigned integer (IV): ($sv->FLAGS & SVf_IOK) && ($sv->FLAGS & SVf_IVisUV)
Floating-point number (NV): $sv->FLAGS & SVf_NOK
Downgraded string (PV): ($sv->FLAGS & SVf_POK) && !($sv->FLAGS & SVf_UTF8)
Upgraded string (PV): ($sv->FLAGS & SVf_POK) && ($sv->FLAGS & SVf_UTF8)
You could use similar tricks. But keep in mind,
It'll be very hard to stringify floating point numbers without loss.
You need to properly escape certain bytes (e.g. NUL) in string literals.
A scalar can have more than one value stored in it. For example, !!0 contains a string (the empty string), a floating point number (0) and a signed integer (0). As you can see, the different values aren't even always equivalent. For a more dramatic example, check out the following:
$ perl -E'open($fh, "non-existent"); say for 0+$!, "".$!;'
2
No such file or directory
It is more complicated. Perl changes the internal representation of a variable depending on the context the variable is used in:
perl -MDevel::Peek -e '
$x = 1; print Dump $x;
$x eq "a"; print Dump $x;
$x .= q(); print Dump $x;
'
SV = IV(0x794c68) at 0x794c78
REFCNT = 1
FLAGS = (IOK,pIOK)
IV = 1
SV = PVIV(0x7800b8) at 0x794c78
REFCNT = 1
FLAGS = (IOK,POK,pIOK,pPOK)
IV = 1
PV = 0x785320 "1"\0
CUR = 1
LEN = 16
SV = PVIV(0x7800b8) at 0x794c78
REFCNT = 1
FLAGS = (POK,pPOK)
IV = 1
PV = 0x785320 "1"\0
CUR = 1
LEN = 16
There's no way to find this out using pure perl. Data::Dumper uses a C library to achieve it. If forced to use Perl it doesn't discriminate strings from numbers if they look like decimal numbers.
use Data::Dumper;
$Data::Dumper::Useperl = 1;
print Dumper(['1',1])."\n";
#output
$VAR1 = [
1,
1
];
Based on your comment that this is to determine whether quoting is needed for an SQL statement, I would say that the correct solution is to use placeholders, which are described in the DBI documentation.
As a rule, you should not interpolate variables directly in your query string.
One simple solution that wasn't mentioned was Scalar::Util's looks_like_number. Scalar::Util is a core module since 5.7.3 and looks_like_number uses the perlapi to determine if the scalar is numeric.
The autobox::universal module, which comes with autobox, provides a type function which can be used for this purpose:
use autobox::universal qw(type);
say type("42"); # STRING
say type(42); # INTEGER
say type(42.0); # FLOAT
say type(undef); # UNDEF
When a variable is used as a number, that causes the variable to be presumed numeric in subsequent contexts. However, the reverse isn't exactly true, as this example shows:
use Data::Dumper;
my $foo = '1';
print Dumper $foo; #character
my $bar = $foo + 0;
print Dumper $foo; #numeric
$bar = $foo . ' ';
print Dumper $foo; #still numeric!
$foo = $foo . '';
print Dumper $foo; #character
One might expect the third operation to put $foo back in a string context (reversing $foo + 0), but it does not.
If you want to check whether something is a number, the standard way is to use a regex. What you check for varies based on what kind of number you want:
if ($foo =~ /^\d+$/) { print "positive integer" }
if ($foo =~ /^-?\d+$/) { print "integer" }
if ($foo =~ /^\d+\.\d+$/) { print "Decimal" }
And so on.
It is not generally useful to check how something is stored internally--you typically don't need to worry about this. However, if you want to duplicate what Dumper is doing here, that's no problem:
if ((Dumper $foo) =~ /'/) {print "character";}
If the output of Dumper contains a single quote, that means it is showing a variable that is represented in string form.
You might want to try Params::Util::_NUMBER:
use Params::Util qw<_NUMBER>;
unless ( _NUMBER( $scalar ) or $scalar =~ /^'.*'$/ ) {
$scalar =~ s/'/''/g;
$scalar = "'$scalar'";
}
The following function returns true (1) if the input is numeric and false ("") if it is a string. The function also returns true (-1) if the input is a numeric Inf or NaN. Similar code can be found in the JSON::PP module.
sub is_numeric {
my $value = shift;
no warnings 'numeric';
# string & "" -> ""
# number & "" -> 0 (with warning)
# nan and inf can detect as numbers, so check with * 0
return unless length((my $dummy = "") & $value);
return unless 0 + $value eq $value;
return 1 if $value * 0 == 0; # finite number
return -1; # inf or nan
}
I don't think there is perl function to find type of value. One can find type of DS(scalar,array,hash). Can use regex to find type of value.

Resources