Variable in shell script - bash

#!/bin/bash
i=1
until [ $i -gt 6 ]
do
echo "Welcome $i times."
i=$(( i+1 ))
done
Why we use double () in i=$(( i+1 )),and
why if we change the program to
i=$( i+1 )
or
i++
or
$i=$i+1
, it is not correct?

$( foo ) tries to execute foo as a command in a subshell and returns the result as a string. Since i+1 is not a valid shell command, this does not work.
$(( foo )) evaluates foo as an arithmetic expression.
It's just two similar (but different) syntaxes that do different things.

http://www.linuxtopia.org/online_books/advanced_bash_scripting_guide/dblparens.html
Similar to the let command, the
((...)) construct permits arithmetic
expansion and evaluation. In its
simplest form, a=$(( 5 + 3 )) would
set "a" to "5 + 3", or 8. However,
this double parentheses construct is
also a mechanism for allowing C-type
manipulation of variables in Bash.

Related

How does the double parenthesis construct work in bash?

I read here that double parenthesis allows for C-style variable manipulation. However, it did not work as expected when I tried to compare strings:
(( "a" == "b" )) && echo yes || echo no
# shows yes
I am also confused as to how this works for using variables as booleans. Following the example in the linked answer, I tried the following:
true=1
false=0
a=true
(( a )) && echo yes || echo no
# shows yes
a=false
(( a )) && echo yes || echo no
# shows no
But wouldn't a be a string value of either true or false?
In addition, since a "no error" value (0) is taken as true and any "error" value (non-zero) is taken as false in bash, why does it look like the opposite convention is taken here?
The main thing to take note of is that the double-parenthesis construct allows for arithmetic evaluation and expansion, and is not an inline C interpreter. Thus, only the rules defined in Shell Arithmetic applies, that is, only C operations on integer types work in the double parentheses.
First example: bash constructs expand first
Anything outside the arithmetic operators are expanded first according to bash rules, e.g. quotes, parameter expansion, bash range {1..5} and list{a,b} constructs, before the double parentheses evaluation starts.
In the first example, double quotes cause what's inside to be interpreted as a single word (no effect inside double-paren) and also evaluate things starting with $ (but there's none inside the quotes), so the first example simply becomes (( a == b )).
Thus, the best way to understand how (( )) works is to work out all the bash constructs first in your mind, then plugging it in. You can also write examples to test your assumptions.
Example with parameter expansion taking place:
a=1
b=2
(( a = $b )) # becomes (( a = 2 ))
(( a = b )) # straight arithmetic evaluation of a = b within the double parenthesis
# they produce the same result but how they arrive at the result is different
(( $a = b )) # becomes (( 2 = b ))
# syntax error as that's not a valid expression.
Notes
There are some peculiarities when you compare the closely related $(( )) and (( )) constructs. The former (Arithmetic Expansion) treats the expression as if the expression is within double quotes, while the latter does not, as explained above.
Second example: Variables in the rvalue position expand recursively
There are some subtle rules in Shell Arithmetic:
"The value of a variable is evaluated as an arithmetic expression when it is referenced"
"or when a variable which has been given the integer attribute using declare -i is assigned a value".
"A shell variable that is null or unset evaluates to 0 when referenced by name without using the parameter expansion syntax"
After trying it out for a bit, you will see that this basically means that any variables in the rvalue will be evaluated recursively until it reaches a value that is either an integer, or is an undefined/null variable name:
b=c
c=d
(( a = b ))
echo $a
# gives 0
d=3
(( a = b ))
echo $a
# gives 3
unset d
declare -i a
a=b
echo $a
# gives 0
d=3
a=b
echo $a
# gives 3
You can also play tricks with putting expressions in variables and evaluating it later:
b=2
c=3
d=c
e=b+d
(( a = e ))
echo $a
# gives 5, as it unfolds like a=b+d; a=2+c; a=2+3
So in the example in the question, a evaluated to true, then evaluated to 1 to give the final result.
How does (( )) reverse the interpretation for true and false
(( 0 ))
echo $? # print the return code of the previous call
# prints 1, which indicates error/false in shell
(( 1 ))
echo $?
# prints 0, indicating success/true
(( 2 ))
echo $?
# prints 0
(( -1 ))
echo $?
# prints 0
So the behaviour inside the parentheses is consistent with the C interpretation for true and false, with 0 indicating false and non-zero indicating true. (( )) "converts" false to a return value of 1 and true to a return value of 0.

Get variable outside while loop shell script

I would like the get the content of variable outside the while loop using shell script
For example:
::
count = 5
while [ #count -gt 0]; do
a=3
b=4
if ( a > b)
result = "UP"
else
result = "DOWN"
fi
count=$[ $count - 1]
done
echo $result
$result appears empty every time!
I just want the content of result outside the loop.
Can anyone help on this issue! I know that the variable inseide the loop is executed in sub-shell, but I tried several tips and doesn't works!
Thanks in avdance
Wow you have a huge number of syntax errors in your short script. I have detailed them in the comments below as well as adjusted where the variables are declared to make the loop do something, e.g.
#!/bin/sh
count=5 ## no spaces aroung " = "
a=3 ## a & b never change in loop
b=4
while [ "$count" -gt 0 ]; do ## always quote variables in [ .. ], spaces required
if ((a > b)); then ## there are two ((..)) in arithmetic comparison
result="UP" ## NO spaces around " = "
else
result="DOWN" ## ditto
fi
printf "%d %s\n" "$count" "$result" ## some output helps
count=$((count - 1)) ## use arithmetic ((..)), no $ required inside
((a++)) ## increment a to make it iteresting.
done
First, in shell there is no spaces allowed around the '=' sign during assignments. When using [ .. ] you must have a space after [ and before ], and always quote your variables inside. The quoting isn't required with bash [[ ... ]] or with the arithmetic comparison ((..)).
Every if and elif must be followed by a then. Every for or while must be followed by a do.
When using the arithmetic operator ((..)) (either for an arithmetic operation or comparison) there are two parens required. You can also use the increment and decrement operators ++ and --, e.g. ((a++)) to increment/decrement values within, but it you are assigning the result you must preceded the opening (( with the $, e.g. $((count - 1))
Example Use/Output
$ sh count.sh
5 DOWN
4 DOWN
3 UP
2 UP
1 UP
I think that accounts for most of the syntax issues. If you have further questions, please drop a comment below.

Adding a list of space separated numbers

Currently stuck in a situation where I ask the user to input a line of numbers with a space in between, then have the program display those numbers with a delay, then add them. I have everything down, but can't seem to figure out a line of code to coherently calculate the sum of their input, as most of my attempts end up with an error, or have the final number multiplied by the 2nd one (not even sure how?). Any help is appreciated.
echo Enter a line of numbers to be added.
read NUMBERS
COUNTER=0
for NUM in $NUMBERS
do
sleep 1
COUNTER=`expr $COUNTER + 1`
if [ "$NUM" ]; then
echo "$NUM"
fi
done
I've tried echo expr $NUM + $NUM to little success, but this is really all I can some up with.
Start with
NUMBERS="4 3 2 6 5 1"
echo $NUMBERS
Your script can be changed into
sum=0
for NUM in ${NUMBERS}
do
sleep 1
((counter++))
(( sum += NUM ))
echo "Digit ${counter}: Sum=$sum"
done
echo Sum=$sum
Another way is using bc, usefull for input like 1.6 2.3
sed 's/ /+/g' <<< "${NUMBERS}" | bc
Set two variables n and m, store their sum in $x, print it:
n=5 m=7 x=$((n + m)) ; echo $x
Output:
12
The above syntax is POSIX compatible, (i.e. works in dash, ksh, bash, etc.); from man dash:
Arithmetic Expansion
Arithmetic expansion provides a mechanism for evaluating an arithmetic
expression and substituting its value. The format for arithmetic expan‐
sion is as follows:
$((expression))
The expression is treated as if it were in double-quotes, except that a
double-quote inside the expression is not treated specially. The shell
expands all tokens in the expression for parameter expansion, command
substitution, and quote removal.
Next, the shell treats this as an arithmetic expression and substitutes
the value of the expression.
Two one-liners that do most of the job in the OP:
POSIX:
while read x ; do echo $(( $(echo $x | tr ' ' '+') )) ; done
bash:
while read x ; do echo $(( ${x// /+} )) ; done
bash with calc, (allows summing real, rational & complex numbers, as well as sub-operations):
while read x ; do calc -- ${x// /+} ; done
Example input line, followed by output:
-8!^(1/3) 2^63 -1
9223372036854775772.7095244707464171953

syntax error: unexpected end of file trying to run a simple bash script

I really can't see what the issue is with my script is. I've considered missing quotations or other syntax errors. There's got to be something I'm missing. It's a very simple while loop script...
#!/bin/bash
c=1
while [ $c -le 5 ]
do
echo "Welcone $c times"
c=$(( c++ ))
done
I should mention that I'm running bash in cygwin on windows 7.
thanks for the help
Change:
c=$(( c++ ))
to
(( c=c+1 ))
When Bash sees: (( var)) it will try and 'do some math' on contents... In this case 'c++' == empty string == '0'; c will always be equal to '1' due to 1st assignment...
From the Bash man page on my Linux system (you may need to review this for Cygwin - could be different...):
((expression))
The expression is evaluated according to the rules described below under ARITHMETIC EVALUATION. If the value of the expression is non-zero, the return status is 0; otherwise the return status is 1. This is exactly equivalent to let "expression".
Also:
id++ id--
variable post-increment and post-decrement
++id --id
variable pre-increment and pre-decrement
After a little testing, the 'pre-increment' seems to do what you are after here - note that you may need to declare 'c' as an integer:
typeset -i c=1
while [ $c -le 5 ]
do
echo "Welcone $c times"
c=++c
# (( c=c+1 ))
done

[0]-bash: [: 0*1%8: integer expression expected

Can someone point out what is wrong with the output.
for i in {0..127} ; do
echo -n [$i]
if [ $i*$j%8 -eq 0 ]; then
echo "\n"
fi
mytool -c "read 0x1540:0xa0:$i*$j"
done
I am trying to format the output into rows containing 8 items each.
I tried the suggestion below and modified my code to
for i in {0..8} ; for j in {0..16}; do
echo -n [$i*$j]
if [[ $i*$j%8 == 0 ]]; then
echo
fi
mytool -c "read 0x1540:0xa0:$i*$j"
done
Above with for i in {0..8} ; for j in {0..16}
I am expecting this to be a nested for loop.I am not very sure if this is how I do a nested loop in bash.
Still the output is not as I expect it.
My output looks like
[0]0x3
[1]0x4
[2]0x21
[3]0x1
[4]0x0
[5]0x0
[6]0x4
[7]0x41
[8]0x84
[9]0x80
[10]0x0
[11]0x0
[12]0x3
[13]0x0
[14]0x43
[15]0x49
[16]0x53
[17]0x43
[18]0x4f
[19]0x2d
[20]0x49
[21]0x4e
[22]0x43
[23]0x20
[24]0x0
[25]0x0
[26]0x9
[27]0x3a
[28]0x37
[29]0x34
[30]0x39
[31]0x34
I want [0] to [7] in ROW1
[8] to [15] in ROW2
and so on.
Use (( )) if you want to do math.
if ((i * j % 8 == 0)); then
Given your problem description I suggest a bit of a rewrite.
for i in {0..15}; do
for j in {0..7}; do
echo -n "[$((i * 8 + j))]"
mytool -c "read 0x1540:0xa0:$i*$j"
done
echo
done
The test command ([ is an alias for test, not syntax) requires the expression to be built up from multiple arguments. This means spaces are critical to separate operators and operands. Since each part is a separate argument, you also need to quote the * so that the shell does not expand it as a file glob prior to calling test/[.
if [ "$i" "*" "$j" % 8 -eq 0 ]; then
The test command expects 7 separate arguments here: $i, *, $j, %, -eq, and 0, which it then assembles into an expression to evaluate. It will not parse an arbitrary string into an expression.
As noted by John Kugelman, there are easier ways to accomplish such arithmetic in bash.

Resources