bash variable reevaluation - sh

This is a total newbie question but i'm struggling so I apologize.
I'm using bourne shell for an init script.
I have a variable
A=1
B="Welcome to world #$A"
Somewhere down the script i have a loop that updates A to 2,3,4 etc... How do I get B to get re-evaluated? At present, B stays "Welcome to world #1" the whole time.
Thanks!
UPDATE #1 - some code:
#!/bin/sh
A=1
B="Welcome to #$A"
repeatloop() {
for i in {1..5}
do
A=$i
echo $B
done
}
repeatloop
Output:
Welcome to #1
Welcome to #1
Welcome to #1
Welcome to #1
Welcome to #1
I'm trying to get #2,#3,#4....

You will need to do the assignment to B each time you do the assignment to A:
#!/bin/sh
A=1
B="Welcome to #$A"
repeatloop() {
for i in {1..5}
do
A=$i
B="Welcome to #$A"
echo $B
done
}
repeatloop
By the way #!/bin/sh is not Bash (even if it's a symlink to it).

You can set your B argument as an eval statement. Then just call it inside the loop:
#!/bin/sh
A=1
B='eval echo "Welcome to #$A"'
repeatloop() {
for i in {1..5}
do
A=$i
$B
done
}
repeatloop
Output:
Welcome to #1
Welcome to #2
Welcome to #3
Welcome to #4
Welcome to #5

When you type...
B="Welcome to the world #$A"
the value of $A is expanded before assigning a value to B. Which means that what you've typed there is equivalent to...
B="Welcome to the world #1"
So "re-evaluating" makes no sense, because B doesn't actually have a variable in it.
If you want variables to not be expanded until something is actually referenced, use a function instead:
function B() {
echo "Welcome to the world $1"
}
A=1
welcomeone=$(B $A)
A=2
welcometwo=$(B $A)

The problem is, B is set with the value of what A currently is. If you want it to update, you'll have get it from a function that recreates the value of B with the new value of A.
Think about it in another language like c/java.
int a = 0;
string b = "blah blah blah" + a; // b= "blah blah blah0"
a = 4;
//b still equals "blah blah blah0"

Related

using REXX to access vm370 disks

REXX is completely new to me, I like it so far. I am using SixPack running on Hercules. VM/370 is a nice environment, but I am trying to make it user friendly; filling in scripts for everything that works-so as to not need to repeat my steps.
The file attached below was written to search in ISFP, instead I want it to access disks. It searches for a specified file.
I do not know enough to rewrite a REXX program. It stops at strange places saying "found" this or that. Please, give any suggestions.
/* REXX */
ARG PROGNAME
PROGNAME = STRIP(PROGNAME)
ACCESS_TEMPLATE='A2 Y U'
USE VAR ACCESS_TEMPLATE A2 Y U /* NOT PARSE */
VAR1 = A2
VAR2 = Y
VAR3 = U
IF PROGNAME == '' THEN DO
SAY 'ENTER MEMBER NAME'
FULL PROGNAME
PROGNAME = STRIP(PROGNAME)
IF PROGNAME == '' THEN DO
SAY NO MEMBER ENTERED. EXITING THE PROGRAM
EXIT
END
END
SEARCH.1 = PROD1.LIB
SEARCH.2 = PROD2.LIB
SEARCH.3 = PROD3.LIB
CNT = 3
FND = 'N'
DO I = 1 TO CNT
ACCESS 'VAR1' 'VAR2' 'VAR3'
LIB = LIST.I(PROGNAME)
IF SYSDSN('LIB') == OK THEN DO
FND = 'Y'
TYPE('LIB')
END
END
IF FND == 'N'THEN DO
SAY MEMBERS NOT FOUND IN ANY LIBRARIES
SAY PLEASE CHECK THE MEMBER ENTERED
EXIT
END
This is a bit late but it's good advice for novice REXX programmers...
Right near the top of your program put in this:
SIGNAL ON NOVALUE
and then near the every end...
NOVALUE: SAY 'NOVALUE error at line' SIGL
exit 4
Why? REXX has a "feature" in that every undefined variable resolves to its own name in UPPER case, like this:
myvar1='hi there'
mayvar2=', joe'
say myvar1||myvar2
What you probably intended to SAY was
'hi there, joe'
but instead got
'hi thereMYVAR2'
If you had SIGNAL ON NOVALUE it would have given you an error message which is a lot better. I ALWAYS put this into my code.

How to substitute repeated script statements in multiple functions by a predefined script block or other mean?

I have multiple functions. By the end of the function they all need to go through exactly the same couple of lines of code, doing some logistic work. How can I define these couple of lines of code into a script block outside these functions and be able to use for all functions, either global reference or passed in as an argument(less preferred)? These lines of code will involve both global and local variables. The ideal is whenever I need to update the logistic work content, I can update it in one place, like script block definition, instead of using a function, which seems overkill for a few lines of codes. Thanks.
function A {
...
$var1 = $global:x + 1
Write-Host var1 value is $var1
}
function B {
...
$var1 = $global:x + 1
Write-Host var1 value is $var1
}
$global:x = 0
A
B

Best practice for creating default values in Powershell

I am new to Powershell, but i am curious what is the best practice for creating default variable in Powershell. This is an example, which i am referencing. In case if you just want to initialize default variables, without intention to pass any parameters to function. Which is better way number 1 or 2 or none of them. :)
1.
function test
{
param ([int]$x = 5,[int]$y = 14)
$x * $y
}
2.
function test
{
[int]$x = 5
[int]$y = 14
$x * $y
}
It just depends on your use-case. If you truly never intend to change the variables, #2 is correct.
I think you just need to ask yourself what future use-cases might be. Would changing the values break your function? The ability to supply parameters is very useful, if not now, perhaps in the future.
Basically, if you're using the variables as FINAL, #2 is fine, but in all other cases I would say #1 is more correct.
If your intention is to take parameters to your function, use Param(). By default, undefined values will be $Null
Function Test
{
Param(
[Int]
$X = 5,
[Int]
$Y = 14
)
Return $X * $Y
}
Function Test2
{
$X=5; $Y=14
$X * $Y
}
> Test2
>> 70
> Test
>> 70
> Test 5 20
>> 100

Get variable from shell script in a perl IF statement

This is a follow-up question from Modify text column based on the column before it
I wanna change the starting index of the line processing, say start from the third line. I notice that in order for perl to use the variable in shell, I must export the variable and use $ENV{} in perl, see:
#!/bin/bash
t=3
export t
perl -e 'print $ENV{t}'
perl -lane '$F[3] += sin($F[2]/10 * 4 * atan2 1, 1) if($ENV{t} .. 4);
print "#F"
'test.txt > test_new.txt
Here test.txt is merely the same with the previous question:
A 0.016333 0.003203 0.472723
A 0.016333 0.035228 0.472723
B 0.016333 0.067253 0.472723
B 0.016333 0.099278 0.472723
C 0.016333 0.131303 0.472723
C 0.016333 0.163328 0.472723
However, the $ENV{t} does not work at all: the line processing still starts from the first line. Maybe in IF statement the usage is different??
What should I do to control which line to start?
It's the range operator that's doing it. The particular rule you are using for (3..4) is
If either operand of scalar ".." is a constant expression, that operand is considered true if it is equal (== ) to the current input line number (the $. variable).
Otherwise,
It is false as long as its left operand is false. Once the left operand is true, the range operator stays true until the right operand is true, AFTER which the range operator becomes false again. It doesn't become false till the next time the range operator is evaluated.
When you have a variable for one end point it is being evaluated and is found to be true. So the left end is always true and the operator never gets to be false, and all lines are printed.
As for how to do it, forego the elegance and test explicitly,
if $. >= $ENV{t} and $. <= 4
You can still use the range operator, for a more compact expression
if $.==$ENV{t} .. 4
However, at this point this may be not as clear as a normal test while a tiny gain in performance (if any) may not even be measurable. Thanks to ikegami for bringing this up and for further comments.
#!/bin/bash
t=3
export t
perl -e 'print $ENV{t}'
perl -lane '$F[3] += sin($F[2]/10 * 4 * atan2 1, 1) if(($.>=$ENV{t})&&($.<= 4));
print "#F" 'test.txt > test_new.txt
The above code works! It is great to know the current line number is $.
The result is:
A 0.016333 0.003203 0.472723
A 0.016333 0.035228 0.472723
B 0.016333 0.067253 0.493849581177725
B 0.016333 0.099278 0.503907047205915
C 0.016333 0.131303 0.472723
C 0.016333 0.163328 0.472723

Breaking/Continuing nested for loops in Coffeescript

How can I break/continue nested loops in Coffeescript? E.g. I have something like:
for cat in categories
for job in jobs
if condition
do(this)
## Iterate to the next cat in the first loop
Also, is there a way to wrap the whole second loop as a conditional to another function within the first loop? E.g.
for cat in categories
if conditionTerm == job for job in jobs
do(this)
## Iterate to the next cat in the first loop
do(that) ## Execute upon eliminating all possibilities in the second for loop,
## but don't if the 'if conditionTerm' was met
break works just like js:
for cat in categories
for job in jobs
if condition
do this
break ## Iterate to the next cat in the first loop
Your second case is not very clear, but I assume you want this:
for cat in categories
for job in jobs
do this
condition = job is 'something'
do that unless condition
Use labels. Since CoffeeScript doesn't support them, you need to hack as such:
0 && dummy
`CAT: //`
for cat in categories
for job in jobs
if conditionTerm == job
do this
`continue CAT` ## Iterate to the next cat in the first loop
do that ## Execute upon eliminating all possibilities in the second for loop,
## but don't if the 'if conditionTerm' was met
Coffescript's "break" only breaks the immediate loop and has no way of identifying an outer loop for breakage (annoying!). This following hack works in some instances for breaking out of multiple loops when a condition is met:
ar1 = [1,2,3,4]
ar2 = [5,6,7,8]
for num1 in ar1
for num2 in ar2
console.log num1 + ' : ' + num2
if num2 == 6
breakLoop1 = true; break
break if breakLoop1
# Will print:
# 1 : 5
# 1 : 6
Using anonymous loop with return
do ->
for a in A
for b in B
for c in C
for d in D
for e in E
for f in F
for g in G
for h in H
for i in I
#DO SOMETHING
if (condition)
return true
Coffeescript would never have multiple breaking/continuing statements, you have to stick to ugly and excessive flags polluting your code or try to replace it by do with a lambda and use return as a multiple break.
https://github.com/jashkenas/coffeescript/issues/4254
For checking all elements in an array, maybe lodash's every would be of use?
https://lodash.com/docs#every
for cat in categories
if _.every jobs, conditionTerm
...
I suppose the design of your code is not very good if you want to use inner break/continue.
It seems to me that any programming language doesn`t allow that.
Using labels as someone suggested is also considered as bad style.