Understanding the "let" command - bash

From this tutorial I was looking at the very first example and can't figure out why b would be set to 2
$ let 'b = a' "(a += 3) + $((a = 1)), b++"
$ echo "$a - $b - $?"
4 - 2 - 0
From my understanding, let evaluates the commands from left to right. Thus, b=a is evaluated, but since a is blank at first, this evaluates to 0, which I've tested with
$ let 'b=a'
$ echo $b
0
Next, a += 3, which, since a is blank, evaluates to 3, but then we add $((a = 1)) which to me seems to be setting a back to 1. Finally we add 1 to b. I would expect the output to be 1 - 1 - 0. However, since the output is 4 - 2 - 0, it seems to me like $((a = 1)) is the first statement executed. Can someone explain why this is the case?

bash parses the arguments given before they are passed to the command. In this case, the $() section is evaluated during this initial parse. The value of a is set to 1 and the whole section substituted with 1. So let sees:
b = a (a += 3) + 1, b++
and a is already 1 before that executes.

Your tutorial states Remember that inside arithmetic evaluation contexts, all other expansions are processed as usual (from left-to-right), and the resulting text is evaluated as an arithmetic expression..
So first all oter expansions (subshells with $(something)) are processed, before rhe resulting text is evaluated as an arithmetic expression.

Related

How to generate and sum up a sequence

Basically, what I'm trying to understand is that how to reassign a variable that was already declared before to a new value.
I want to reassign the variable to a different value in the loop. Then print that sum.
For example in JavaScript,
sum = 0;
for... (loop)
sum = sum + start-point; (loop body)
console.log(sum)
Now I don't seem to be able to get that in bash.
This is my code in bash
echo Enter a number:
read NUMBER
echo Enter a startpoint:
read STARTPOINT
echo Enter how many terms:
read TERMS
sum=0;
for ((n=0;n<$TERMS;n++));
do
STARTPOINT=$((STARTPOINT + NUMBER))
sum=$(($sum + $STARTPOINT))
echo $STARTPOINT
echo $sum
done
All the code is correct except the sum part, and because of it the code doesn't run properly. if I remove that part, it works fine. I just need to sum the outputs of the variable STARTPOINT.
Example
Input
NUMBER = 3 (means to increment 3 in the startpoint)
STARTPOINT = 2
TERMS = 5 (means to add 3 to 2 five times)
Expected output
5
8
11
14
17
And the part that I am having difficulty with is how to add all these numbers, when all added, it should print 55.
In this answer I changed your variable names to be lowercase. This is the conventional naming scheme in bash to avoid accidental name collisions with built-in variables and environment variables.
If I understood correctly, you want to build the following sequence and also sum it up:
startpoint + 1*number + startpoint + 2*number + ... + startpoint+ term*number
In that case you should not change startpoint inside your loop. Use another variable to store the result of startpoint + n*number.
startpoint=2 number=3 terms=5 sum=0
echo "$terms times increment $startpoint by $number:"
for ((n=1; n<=terms; n++));
do
((addend = startpoint + n*number))
echo "$addend"
((sum += addend))
done
echo "The sum is $sum"
However, instead of using a slow loop you could printing the sequence using seq and then calculate its sum using the closed formula for triangular numbers:
startpoint=2 number=3 terms=5
seq $((startpoint+number)) $number $((startpoint+terms*number))
((sum = terms*startpoint + terms*(terms+1)/2 * number))
echo "The sum is $sum"

What is the difference between variable post-increment/decrement and pre-increment/decrement in bash?

ARITHMETIC EVALUATION section in bash manual lists following operators among others:
id++ id--
variable post-increment and post-decrement
++id --id
variable pre-increment and pre-decrement
As I understand, ++x and --xx increase or decrease the variable before other operations are performed? For example x++:
$ x=5; echo $(( ++x / 2 ))
3
$ x=5; echo $(( x++ / 2 ))
2
$
However, when are x++ and x-- useful? And in general what is the difference between variable post-increment/decrement and pre-increment/decrement in bash?
Quoting from Increment and decrement operators:
In languages that support both versions of the operators, the
pre-increment and pre-decrement operators increment (or decrement) their operand by 1, and the value of the expression is the resulting
incremented (or decremented) value. In contrast, the post-increment
and post-decrement operators increase (or decrease) the value of
their operand by 1, but the value of the expression is the operand's
original value prior to the increment (or decrement) operation.
So, you'll find:
$ x=5; echo $(( x++ / 2 ))
2
$ echo ${x} // The effect of post-increment is visible here
3
Both post-ops and pre-ops change (increase/decrease) value of the variable.
The difference is what they evaluate to: pre-ops evaluate to the value of the variable after the change, and post-ops - to the value before the change.
When the evaluated value isn't used, there is no difference. I.e. these two lines have the same effect:
((x++))
((++x))
Both pre-ops and post-ops are used to remove the need for an explicit assignment. I.e. make code shorter. So, instead of writing this:
x=$((x + 1))
y=$((x * 5))
You can write this:
y=$((++x * 5))
Conversely, instead of this:
y=$((x * 5))
x=$((x + 1))
You can write this:
y=$((x++ * 5))
Most often these operations are used in loop bodies and loop control expressions.

Why parentheses change returning value?

I'm learning ruby and I got stuck with probable simple problem. There is the code:
str = 'abc'
a = 1
b = 2
a = str.reverse if str.size > 3
b = (str.reverse if str.size > 3)
p a
p b
Output:
1
nil
Can somebody tell me what these parentheses change in return value ? Is it kind of "block" of code ?
They are two different statements.
The first one is a conditional assignment:
a = str.reverse if str.size > 3
The if applies to the whole line. If str.size is not greater than 3, then absolutely nothing happens; a is not touched at all. You could also write it this way:
if str.size > 3 then
a = str.reverse
end
Being able to stick the if on the end just lets you do it in one line instead of a block.
The second one is an assignment of a conditional value.
b = (str.reverse if str.size > 3)
The value of b will always be changed in this case, no matter what; the value of str.size just determines what it is changed to. You could also use the block-form of if here:
b = if str.size > 3 then
str.reverse
end
The important difference is that the assignment to b happens outside the if, so it's not conditional; it always happens.
Parentheses don't create blocks, but they do determine precedence. Whenever you have a statement that could be interpreted multiple ways depending on the order in which things happen, what actually happens is determined by precedence rules. For instance, standard arithmetic rules tell us that this is true:
3 + 2 × 4 = 11
The answer isn't 20 because multiplication has precedence over addition. Parentheses let you change that; the above is equivalent to
3 + (2 × 4) = 11
and if you want the answer to be 20, then you could write this instead:
(3 + 2) × 4 = 20
Same thing goes for Ruby. Without parentheses, the first line is equivalent to this parenthesized version:
(a = str.reverse) if str.size > 3
which makes it clear that the assignment is what is guarded by the condition, not just the value being assigned.
Parentheses will change priority during operation.
Code between parenthesis will be evaluated as boolean due to its status as conditional expression.
Like #Mark Reed said, there are differents statements. And it is up to your intention to use the correct one. Hope it helped. ;)

Array with only even number

for X in {18..2500} ; is one line of my script, which means to pick number one by one like: 18,19,20,21,22,23....till 2500
However I find I only need even number right now: 18,20,22,24.....2500
Then what should I do by a slight modify of the line?
Thanks
edit:
It's bash...
My script is now changed to:
#!/bin/bash
TASK=1101;
NUM=9;
TEND=1100;
for X in {18..2500};{
if (X % 2 == 0);
do
echo "$X echo \"Wait until $NUM job is done\" $NUM" ;
NUM=$((NUM+2)) ;
X=$((X+1)) ;
TEND=$((TEND+100)) ;
echo "$X -t $TASK-$TEND jobs.sh" ;
TASK=$((TASK+100)) ;}
done
but got errors like:
line 15: syntax error near unexpected token `do'
You can specify the increment:
for X in {18..2500..2}
A sequence expression takes the form {x..y[..incr]}, where x and y are
either integers or single characters, and incr, an optional increment, is
an integer.
Or
for X in `seq 18 2 2500`
This is not C++. This is a bash script.
Your for-loop needs to start with a do:
for X in {18..2500}; do
Your if-statement syntax looks off. It should probably be something like this, note the then:
if [[ $((X % 2)) == 0 ]]; then
if-blocks end with:
fi
And the for-do block ends with:
done
Better still... do away with the if statement and use Bash's for-loop construct to generate only even numbers:
for ((X = 18; X <= 2500; X += 2)); do
echo "$X echo \"Wait until $NUM job is done\" $NUM" ;
# ...
done
Try the modulus operator. In almost all languages, it'll look something like this:
if (x % 2 == 0) // …Do something
That is valid C code, but can easily be applied to other languages.
You can think of the mod operator as a division sign placed in the same location, but rather than returning the results of the division it returns the remainder. Therefore in this code, if the remainder of a divide-by-two is 0, then it divides evenly by two, and so it's even by definition.
If your language has a for(;;) syntax you can
for (X = 18; X <= 2500; X += 2)
There are a couple things you can do:
use the modulus function for your language:
for x in {18..2500} {
if (x mod 2=0) {
do something;}
step through your For loop 2 at a time:
for x in {18..2500} step 2 {
do something;}

Why does let --n have an exit code different from let n-- in bash?

Why do these have an exit code of 1?
n=1
let --n
And why does this have exit code of 0?
n=1
let n--
This has exit code of 1 too... why?
n=1
let "n = n - 1"
man bash says:
let arg [arg ...]
Each arg is an arithmetic expression to be evaluated (see ARITH‐
METIC EVALUATION above). If the last arg evaluates to 0, let
returns 1; 0 is returned otherwise.
1 - 1 = 0, therefore the exit code is 1.
The difference between --n and n-- is that the first term has the value n-1 (it's decremented first and then evaluated) while the second term has the value n (post decrement). After the value is taken, n is always one less but the position of the -- says when the value should be copied into the result.

Resources