I am confused with a tcsh shell script issue. (for work, no choice in shell, I'm stuck with it)
The enableThingN items below are shell enviroment variables set by other things before running this csh script, using tcsh shell. These are not set within the same script here at all, only evaluated here.
Error message is:
enableThing1: Undefined variable.
Code is:
if ( ( $?enableThing1 && ($enableThing1 == 1) ) || \
( $?enableThing2 && ($enableThing2 == 1) ) || \
( $?enableThing3 && ($enableThing3 == 1) ) || \
( $?enableThing4 && ($enableThing4 == 1) ) ) then
set someScriptVar = FALSE
else
set someScriptVar = TRUE
endif
So, as I understand things, the first part of the big if condition is to check if enableThing1 is defined at all or not, using the $?enableThing1 magic. If it is defined, then move on and check the value is 1 or something else. If not defined, then skip the ==1 part of the check for the same shell variable, and move on to see if enableThing2 is defined at all or not, and so on.
As it seems like I am checking for existence, and intend to avoid checking value if it is not defined at all, where have I gone wrong?
I have searched here on stackoverflow and on Google at large, but there are few results and don't get me to an answer, such as:
https://stackoverflow.com/questions/16975968/what-does-var-mean-in-csh
An if statement to check the value of the variable requires that the variable exists.
if ( ( $?enableThing1 && ($enableThing1 == 1) ) || \
# ^ this will fail if the variable is not defined.
So the if condition turns into
if ( ( 0 && don'tknowaboutthis ) || \
and it falls flat.
Assuming you don't want an if ladder, and functionality to add to this list of variables to check for, you can try the following solution:
#!/bin/csh -f
set enableThings = ( enableThing1 enableThing2 enableThing3 enableThing4 ... )
# setting to false initially
set someScriptVar = FALSE
foreach enableThing ($enableThings)
# since we can't use $'s in $? we'll have to do something like this.
set testEnableThing = `env | grep $enableThing`
# this part is for checking if it exists or not, and if it's enabled or not
if (($testEnableThing != "") && (`echo $testEnableThing | cut -d= -f2` == 1 )) then
# ^ this is to check if the variable is defined ^ this is to take the part after the =
# d stands for delimiter
# for example, the output of testEnableThing, if it exists, would be enableThing1=1
# then we take that and cut it to get the value of the variable, in our example it's 1
# if it exists and is enabled, set your someScriptVar
set someScriptVar = TRUE
# you can put a break here since it's irrelevant to check
# for other variables after this becomes true
break
endif
end
This works because we are only working with one variable, "testEnableThing", which is always defined due to the way this works. It can be a blank string, but it will be defined so our if statement won't fall flat.
Hope this solves it for you.
Related
I am unable to figure out a practice question which is asking to create a lua file to check whether a number is even or odd by using redis queries as well.
The code that I have written in lua file is like
local myval = 94
if (myval % 2 == 0 ) then
redis.call()
else
redis.call()
end
Now as a part of the statement the question is asking like below
Declare a local variable with name myval and assign 94 to it. maintain
key value as number.
I am unable to understand the second part of this statement. How should I set the key value along with the local variable declaraion in lua file.
maintain key value as number.
Can you please help me to understand this? what needs to be done here?
finally, my code will look like this, which will set the key-value depends on the myval number.
local myval = 94
if (myval % 2 == 0 ) then
redis.call(‘set’, KEYS[1], ARGV[1])
else
redis.call(‘set’, KEYS[1], ARGV[1])
end
Hard to tell what you're trying to accomplish without more details.
This should retrieve odd or even value stored in the 94th position.
Might need to flip ARGV numbers; haven't seen what's in database, or expected output.
local myval = 94
if ( myval %2 == 0 ) then -- even
print( redis .call( ‘get’, KEYS[myval], ARGV[1] ) )
else
print( redis .call( ‘get’, KEYS[myval], ARGV[2] ) )
end
I am using read.csv.sql to conditionally read in data (my data set is extremely large so this was the solution I chose to filter it and reduce it in size prior to reading the data in). I was running into memory issues by reading in the full data and then filtering it so that is why it is important that I use the conditional read so that the subset is read in, versus the full data set.
Here is a small data set so my problem can be reproduced:
write.csv(iris, "iris.csv", row.names = F)
library(sqldf)
csvFile <- "iris.csv"
I am finding that the notation you have to use is extremely awkward using read.csv.sql the following is the how I am reading in the file:
# Step 1 (Assume these values are coming from UI)
spec <- 'setosa'
petwd <- 0.2
# Add quotes and make comma-separated:
spec <- toString(sprintf("'%s'", spec))
petwd <- toString(sprintf("'%s'", petwd))
# Step 2 - Conditionally read in the data, store in 'd'
d <- fn$read.csv.sql(csvFile, sql='select * from file where
"Species" in ($spec)'
and "Petal.Width" in ($petwd)',
filter = list('gawk -f prog', prog = '{ gsub(/"/, ""); print }'))
My main problem is that if any of the values above (from UI) are null then it won't read in the data properly, because this chunk of code is all hard coded.
I would like to change this into: Step 1 - check which values are null and do not filter off of them, then filter using read.csv.sql for all non-null values on corresponding columns.
Note: I am reusing the code from this similar question within this question.
UPDATE
I want to clear up what I am asking. This is what I am trying to do:
If a field, say spec comes through as NA (meaning the user did not pick input) then I want it to filter as such (default to spec == EVERY SPEC):
# Step 2 - Conditionally read in the data, store in 'd'
d <- fn$read.csv.sql(csvFile, sql='select * from file where
"Petal.Width" in ($petwd)',
filter = list('gawk -f prog', prog = '{ gsub(/"/, ""); print }'))
Since spec is NA, if you try to filter/read in a file matching spec == NA it will read in an empty data set since there are no NA values in my data, hence breaking the code and program. Hope this clears it up more.
There are several problems:
some of the simplifications provided in the link in the question were not followed.
spec is a scalar so one can just use '$spec'
petwd is a numeric scalar and SQL does not require quotes around numbers so just use $petwd
the question states you want to handle empty fields but not how so we have used csvfix to map them to -1 and also strip off quotes. (Alternately let them enter and do it in R. Empty numerics will come through as 0 and empty character fields will come through as zero length character fields.)
you can use [...] in place of "..." in SQL
The code below worked for me in both Windows and Ubuntu Linux with the bash shell.
library(sqldf)
spec <- 'setosa'
petwd <- 0.2
d <- fn$read.csv.sql(
"iris.csv",
sql = "select * from file where [Species] = '$spec' and [Petal.Width] = $petwd",
verbose = TRUE,
filter = 'csvfix map -smq -fv "" -tv -1'
)
Update
Regarding the update at the end of the question it was clarified that the NA could be in spec as opposed to being in the data being read in and that if spec is NA then the condition involving spec should be regarded as TRUE. In that case just expand the SQL where condition to handle that as follows.
spec <- NA
petwd <- 0.2
d <- fn$read.csv.sql(
"iris.csv",
sql = "select * from file
where ('$spec' == 'NA' or [Species] = '$spec') and [Petal.Width] = $petwd",
verbose = TRUE,
filter = 'csvfix echo -smq'
)
The above will return all rows for which Petal.Width is 0.2 .
So, suppose I'm trying to read in a file the length of which I don't know before hand. We can use iostat and a while loop to break when we need to, but I'm having an issue with this. Namely, the code I've written reads the last line twice. I'm sure there is an obvious solution, but I can't seem to figure it out. I don't really understand how either the read() or iostat functions work entirely (I'm pretty new at fortran), but I can't glean much from documentation so I'm hoping someone here can help.
Here is the (relevant bit of) code I've written:
filename = 'test.txt'
iostat_1 = 0
iostat_2 = 0
open(newunit = lun, file = filename, status = 'old', iostat = iostat_1)
if (iostat_1 == 0) then
do while(iostat_2 == 0)
if(iostat_2 == 0) then
read(lun,*,iostat = iostat_2) dum, real_1,real_2,int_1
print *, dum, real_1,real_2,int_1
endif
enddo
endif
So, supposing my input file is
1 1.0 1.0 1
2 2.0 2.0 1
3 3.0 3.0 1
4 4.0 4.0 4
Then the output to the terminal from the print statement will be
1 1.0 1.0 1
2 2.0 2.0 1
3 3.0 3.0 1
4 4.0 4.0 4
4 4.0 4.0 4
So keep in mind the following: The main purpose here is to be able to read in a file with an arbitrary number of lines. I'm not interested in a solution involving reading the number of lines first.
Thanks for the help!
UPDATE Okay I just solved the problem. That being said, I'm wondering if there is a solution less clumsy than mine. Here is what I did to fix the issue
! Body of ReInsert
filename = 'rpriov3.dat'
iostat_1 = 0
iostat_2 = 0
open(newunit = lun, file = filename, status = 'old', iostat = iostat_1)
if (iostat_1 == 0) then
do while(iostat_2 == 0)
if(iostat_2 == 0) then
read(lun,*,iostat = iostat_2) dum, real_1,real_2,int_1
if(iostat_2 == 0) then !<---- Added this nested if statement
print *, dum, real_1,real_2,int_1
endif
print *, iostat_2
endif
enddo
endif
As you found out, when you set an iostat parameter, the read command doesn't overwrite the variables it asks for.
Your solution is, as you already noticed, somewhat convoluted.
Firstly:
do while (condition)
if (condition) then
...
end if
end do
In this case, the inner if statement is complete surplus. The loop doesn't run unless condition is true, so unless the evaluation of condition itself doesn't change the result 1), the if clause will always be executed.
The second thing I'd look at is: What should happen if the open fails? In most cases, I want to print an error and quit:
open(..., iostat=ios)
if (ios /= 0) then
print*, "Error opening file"
STOP 1
end if
do while (...)
...
end do
Even if you don't want to exit the program in case of an error in open, there are usually ways to make the code more readable than eternal nesting. For example, you could ask the user for a filename again and again (in its own loop) for a file name, until either the file opens, or the user enters some quit message.
ios = 1
do while (ios /= 0)
write(*, *, advance='no') "Enter filename (or 'quit') :"
read(*, *) filename
if ( trim(filename) == "quit" ) STOP
open(newunit=lun, file=filename, ..., iostat=ios)
end do
Finally there's the most inner if block. Since you want to exit the loop anyway when you reach an error, you can use the exit statement inside a loop to exit it immediately without executing the rest of the loop block:
do
read(..., iostat=ios) ...
if (ios /= 0) exit
print *, ....
end do
This is an infinite loop with an explicit exit as soon as it encounters a read error (usually, but not necessarily an EOF). Since the print statement is after the exit, it won't be executed in case of such an error.
1) What I mean by that is something like this C snippet i++ < 10, which both tests i against 10 and increments it.
I am working on a text based adventure game for a few friends and I to work through in batch. I'd like to make the option to have an entire section of variables echoed when wanted. Below is the entry code. The first section, Variables, is not an accessed section, but creates variables prior to the game starting. The second section, stats, provides these variables echoed.
#ECHO OFF
::Variables
set Name=Und
set Gender=Und
set Age=Und
set Gold=0
set Hunger=Satisfied
set Illness=None
set Wounds=None
set CHP=10
set MHP=10
set CMP=0
set MMP=0
goto Start
::Stats
:Stats
cls
echo Name: %Name%
echo Gender: %Gender%
echo Age: %Age%
echo Gold: 0
echo.
echo Health
echo Hunger: %Hunger%
echo Illness: %Illness%
echo Wounds: %Wounds%
echo.
echo Stats
echo HP: %CHP%/%MHP%
echo MP: %CMP%/%MMP%
My current solution to this issue is using
set /p "situation"= :
if "%situation%"=="1" goto nextpart
if "%situation%"=="2" goto nextpart
if "%situation%"=="3" goto nextpart
if "%situation%"=="Stats" goto Stats.
The issue with this method, however, is that once in stats, I have no way to jump to the previous section. It would require me making an exit gateway to every possible section I've created. So the question:
Can I have a series of variables echoed without leaving the current section?
You can use Call:stats in place of goto:stats.
Like this it will comme back in the current section
Edit :
#echo off
set $var=1000
call:aff
echo done
exit/b
:aff
echo %$var%
You can get Lua here
There's a somewhat out of date version of the Lua book here.
Here's an example of how you could write this in Lua:
-- create a bunch of variables
Name = nil
Gender = nil
Age = nil
Gold = 0
Hunger = 'Satisfied'
Illness = 'None'
Wounds = 'None'
CHP = 10
MHP = 10
CMP = 0
MMP = 0
-- create a routine whose job it is to print the variables
function stats()
print(' Name: ' .. Name )
print('Gender: ' .. Gender )
print(' Age: ' .. Age )
print(' Gold: ' .. Gold )
print('Health' )
print(' Hunger: ' .. Hunger )
print('Illness: ' .. Illness )
print(' Wounds: ' .. Wounds )
print('' )
print('Stats' )
print(' HP: ' .. CHP .. '/' .. MHP )
print(' MP: ' .. CMP .. '/' .. MMP )
end
-- call the routine
stats()
This appears more verbose that your BAT code, but once your game logic starts to get complicated, using a real programming language will make it much easier and more rewarding.
The easy way to arrange a save/restore regime in batch is to reserve a prefix for the variables to be saved/restored. For instance, if you were to reserve $ for the variables of interest, then
set $>savefile
is all you'd need to save all of the $ variables to a file and
for /f "delims=" %%a in (savefile) do set %%a
would restore them.
I have an SQL file which will give me an output like below:
10|1
10|2
10|3
11|2
11|4
.
.
.
I am using this in a Perl script like below:
my #tmp_cycledef = `sqlplus -s $connstr \#DLCycleState.sql`;
after this above statement, since #tmp_cycledef has all the output of the SQL query,
I want to show the output as:
10 1,2,3
11 2,4
How could I do this using Perl?
EDIT:
I am using the following code:
foreach my $row (#tmp_cycledef)
{
chomp $row;
my ($cycle_code,$cycle_month)= split /\s*\|\s*/, $row;
print "$cycle_code, $cycle_month\n";
$hash{$cycle_code}{$cycle_month}=1
}
foreach my $num ( sort keys %hash )
{
my $h = $hash{$num};
print join(',',sort keys %$h),"\n";
}
the fist print statement prints:
2, 1
2, 10
2, 11
2, 12
3, 1
3, 10
3, 11
but the out is always
1,10,11,12
1,10,11,12
1,10,11,12
1,10,11,12
1,10,11,12
1,10,11,12
1,10,11,12
Well, this one is actually how you might do it in perl:
# two must-have pragmas for perl development
use strict;
use warnings;
Perl allows for variables to be created as they are used, $feldman = some_function() means that you now have the variable $feldman in your local namespace. But the bad part about this is that you can type $fldman and take a long time finding out why what you thought was $feldman has no value. Turning on strictures means that your code fails to compile if it encounters an undeclared variable. You declare a variable with a my or our statement (or in older Perl code a use vars statement.
Turning on warnings just warns you when you're not getting values you expect. Often warnings tends to be too touchy, but they are generally a good thing to develop code with.
my %hash; # the base object for the data
Here, I've declared a hash variable that I creatively called %hash. The sigil (pronounced "sijil") "%" tells that it is a map of name-value pairs. This my statement declared the variable and makes it legal for the compiler. The compiler will warn me about any use of %hsh.
The next item is a foreach loop (which can be abbreviated "for"). The loop will process the list of lines in #tmp_cycledef assigning each one in turn to $row. ( my $row).
We chomp the line first, removing the end-of-line character for that platform.
We split the line on the '|' character, creating a list of strings that had been separated by a pipe.
And then we store it in a two-layered hash. Since we want to group them by at least the first number. We could do this by array, and create an array at the location in the hash like so: push #{$hash{$key}}, $val, but I typically want to collapse duplicates (not that there were any duplicates in your sample.)
Here:
foreach my $row ( #tmp_cycledef ) {
chomp $row; # removes the end-of-line character when present.
my ( $key, $val ) = split /\|/, $row;
# One of the best ways to merge lists is a presence-of idea
# with the hash holding whether the value is present
$hash{$key}{$val} = 1;
}
Once we have the data in the structure, we need to iterate both level of hash keys. You wanted to separate the "top level" numbers by lines, but you wanted the second numbers concatenated on the same line. So we print a line for each of the first numbers and join the list of strings stored for each number on the same line, delimited by commas. We also sort the list: { $a <=> $b } just takes to keys and numerically compares them. So you get a numeric order.
# If they were alpha keys our sort routine, we would just likely say sort keys %hash
foreach my $num ( sort { $a <=> $b } keys %hash ) {
my $h = $hash{$num};
print "$num ", join( ',', sort { $a <=> $b } keys %$h ), "\n";
}
As I said in the comments, sort, by default, sorts in character order so you can just say sort keys %hash.
To help you out, you really need to read some of these:
strictures
warnings
perldata
perlfunc -- especially my, foreach, chomp, split, keys, sort and join
And the data structure tutorial
Use a hash of arrays to collect all the values for a single key together, then print them out:
init hash
for each line:
parse into key|value
append value to hash[key]
for each key in hash: # you can sort it, if needed
print out key, list of values
If your input is sorted (as it is in the provided sample), you don't actually need to bother with the hash of arrays/hashes. The code is a bit longer, but doesn't require you to understand references and should run faster for large datasets:
#!/usr/bin/perl
use strict;
use warnings;
my #tmp_cycledef = <DATA>;
my $last_key;
my #values;
for (#tmp_cycledef) {
chomp;
my ($key, $val) = split '\|';
# Seed $last_key with the first key value on the first pass
$last_key = $key unless defined $last_key;
# The key has changed, so it's time to print out the values associated
# with the previous key, then reset everything for the new one
if ($key != $last_key) {
print "$last_key " . join(',', #values) . "\n";
$last_key = $key;
#values = ();
}
# Add the current value to the list of values for this key
push #values, $val;
}
# Don't forget to print out the final key when you're done!
print "$last_key " . join(',', #values) . "\n";
__DATA__
10|1
10|2
10|3
11|2
11|4