Get variable outside while loop shell script - shell

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.

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.

How to disable special characters in bash? [duplicate]

This question already has an answer here:
Prevent globbing in a bash script
(1 answer)
Closed 6 years ago.
I've created a script which performs a calculation. For example:
count 1 + 3
1 + 3 = 4,
- and / also works, but if I type
count 1 * 3, I got
Should be number operand number
Here is a part of a script:
if [ "$#" -ne 3 ]; then
echo "Should be number operand number"
exit 1
fi
...
elif [ "$2" = "*" ]; then
result=`echo $1 \* $3 | bc`
echo "$1 * $3 = $result"
Yes, if I type \ before * in command line it would work, but I need it to run just with a *.
I've tried to use set -f inside the script it did not work (yes it disables special characters if I type in bash itself but it is not what I need). There is also a shopt command that controls shell behavior, so I've tried to enable some options like shopt -s globstar, nullglob, promptvars, etc. It did not work. I put the command before if statement maybe it is wrong.
I would appreciate if someone corrects me or tells the other way to disable interpretation of a special character from inside the script.
This exact problem is mentioned in the POSIX specification of expr, saying that the tool has a "rather difficult syntax" for the reason you describe.
This is definitely a known problem, in other words, and POSIX itself leaves it unsolved. You should really consider working with Unix and do the same, rather than trying to work against it.
Some options are:
Require the entire expression to one quoted argument, e.g. count "1 * 2"
This is a pragmatic, fail-fast way of ensuring that the argument is always quoted, so that any introduction of * still works. The bash builtin let does this.
read the expression yourself, e.g. just run count and then type in 1 * 2.
This avoids the shell touching your data, so that you can interpret it however you want. It's what bc and dc do.
Live with the fact that passing * will require careful quoting.
This is what expr does.
Use a magic alias hack that only works on certain shells under very specific circumstances.
This is what no one does, because it's fragile and unpredictable, even though it in some cases allows the syntax you want.
Always quote when you echo. You need to quote the variable reference as well:
me$ FOO="BAR * BAR"
me$ echo "$FOO"
BAR * BAR
If you don't use double quote, bash will see * and perform filename expansion. * expands to all files in your current directory.
However in saying this you would have to be using single quotes for the *:
#!/bin/bash
if [ "$2" = "*" ]; then
result=`echo $1 \* $3 | bc`
echo "$1 * $3 = $result"
fi
The following would work:
ยป count.sh 2 '*' 1
2 * 2 = 4
There is nothing you can do in your program, because the * expansion is done by the shell (in contrast to Windows, where it's done by the program).
Here's another code example using a menu-driven approach instead:
#!/bin/bash
while true; do
read -p "what's the first number? " n1
read -p "what's the second number? " n2
PS3="what's the operation? "
select ans in add subtract multiply divide; do
case $ans in
add) op='+' ; break ;;
subtract) op='-' ; break ;;
multiply) op='*' ; break ;;
divide) op='/' ; break ;;
*) echo "invalid response" ;;
esac
done
ans=$(echo "$n1 $op $n2" | bc -l)
printf "%s %s %s = %s\n\n" "$n1" "$op" "$n2" "$ans"
done
what's the first number? 5
what's the second number? 4
1) add
2) subtract
3) multiply
4) divide
what's the operation? /
invalid response
what's the operation? 4
5 / 4 = 1.25000000000000000000
In short, quote everything where you do not require the shell to perform token splitting and wildcard expansion.

I am getting expr syntax errors in bash shell for a simple program

#!/bin/bash
clear
echo "Enter a number"
read a
s = 0
while [ $a -gt 0 ]
do
r = ` expr $a % 10 `
s = ` expr $s + $r `
a = ` expr $a / 10 `
done
echo "sum of digits is = $s"
This is my code guys .
I am getting a bunch of expr syntax errors.
I am using the bash shell.
Thanks!
Your error is caused by the spaces surrounding the = in the assignments, the following replacements should work (I prefer $() to using backticks since they're much easier to nest):
s=0
r=$(expr $a % 10)
s=$(expr $s + $r)
a=$(expr $a / 10)
For example, s = 0 (with the spaces) does not set the variable s to zero, rather it tries to run the command s with the two arguments, = and 0.
However, it's not really necessary to call the external expr1 to do mathematical manipulation and capture the output to a variable. That's because bash itself can do this well enough without resorting to output capture (see ARITHMETIC EVALUATION in the bash man page):
#!/bin/bash
clear
read -p "Enter a number: " number
((sum = 0))
while [[ $number -gt 0 ]]; do
((sum += number % 10))
((number /= 10))
done
echo "Sum of digits is $sum"
You'll notice I've made some other minor changes which I believe enhances the readability, but you could revert back to the your original code if you wish and just use the ((expression)) method rather than expr.
1 If you don't mind calling external executables, there's no need for a loop in bash, you could instead use sneakier methods:
#!/bin/bash
clear
read -p "Enter a number: " number
echo "Sum of digits is $(grep -o . <<<$number | paste -sd+ | bc)"
But, to be brutally honest, I think I prefer the readable solution :-)

[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.

Variable in shell script

#!/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.

Resources