1

When I am executing the below script, I am getting the following error :-
The script executes infintely and below line is printed everytime. "line 9: 1=1+2: command not found". Why?

#!/bin/bash

echo "Script 1 - Linux Scripting Book"
x=1

while [ $x -le 45 ]
do
        echo x : $x
        $x=$x+2
done

echo "End Of Script 1"

exit 0

Also if I change the $x=$x+2 to x+$x+2 then also I am getting the below error.

line 6: [: 1+2: integer expression expected

Same script when executed like this runs fine.

#!/bin/bash

echo "Script 1 - Linux Scripting Book"
x=1

while [ $x -le 45 ]
do
        echo x : $x
        let x=x+2
done

echo "End Of Script 1"

exit 0
John Doe
  • 2,752
  • 5
  • 40
  • 58

3 Answers3

3

You get line 9: 1=1+2: command not found because 1=1+2 is what $x=$x+2 is expanded into.

Use expr or let or ((...)) for integer calculations and bc for floating point:

let x=x+2
((x=x+2))           #same as above
((x+=2))            #same
((x++))             #if adding just one
((++x))             #if adding just one
x=$((x+2))
x=`expr $x + 2`     #space before and after +
x=$(echo $x+2|bc)   #using bc
x=$(echo $x+2.1|bc) #bc also works with floating points (numbers with decimals)
Kjetil S.
  • 3,468
  • 20
  • 22
  • what about x=$x+2 – John Doe Mar 24 '18 at 08:23
  • [`((..))`](https://www.gnu.org/software/bash/manual/html_node/Conditional-Constructs.html#Conditional-Constructs) isn't the easiest feature to find in the manual. It does the same thing as `let`. – Yann Vernier Mar 24 '18 at 08:27
  • 1
    @YannVernier but you'll find out that `let` is never used by experimented people. It seems you haven't grasped the idioms of bash scripting yet. – gniourf_gniourf Mar 24 '18 at 08:36
  • You mean experienced, but shell scripters aren't so unified that this is a clear idiom. Personally, I'd rather see a script fail with `let: not found` than trying to execute `x=x+2` as an external command. – Yann Vernier Mar 24 '18 at 08:41
  • The [bash hackers wiki](http://wiki.bash-hackers.org/commands/builtin/let#examples) recommends the compound command over let, so I guess that'll stand as a style guide. The main reason would be to avoid `*` globbing. – Yann Vernier Mar 24 '18 at 08:48
  • POSIX shell does specify [arithmetic expansion](http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_04), so the `x=$((x+2))` variant is the most portable of these next to the external `expr` call. It executes inside the shell even in dash or busybox ash, but is not [bash specific](https://mywiki.wooledge.org/Bashism). The `declare -i` variant is probably a winner in concise legibility. – Yann Vernier Mar 24 '18 at 09:05
  • @JohnDoe `x=1` then `x=$x+2` then `echo $x` will just output `1+2` not `3`. – Kjetil S. Mar 24 '18 at 09:25
2

Because that's not the Bourne shell syntax for setting a variable; it looks more like Perl or PHP. The $ is used for parameter expansion and is not part of the variable name. Variable assignment simply uses =, and let evaluates arithmetic expressions (much like $((expression))). Another syntax that should work is x=$((x+2)). Note that these arithmetic evaluations are a bash feature; standard unix shells might require use of external tools such as expr.

Yann Vernier
  • 15,414
  • 2
  • 28
  • 26
  • It works, it just doesn't perform arithmetic evaluation (so breaks your `[ ]` test). [`declare -i`](https://www.gnu.org/software/bash/manual/html_node/Bash-Builtins.html#index-declare) can enable arithmetic evaluation for particular variables. Oddly, `test` claims it wants an expression but really expects just a number. – Yann Vernier Mar 24 '18 at 08:18
  • 1
    Please don't recommend `seq`. `seq` is non-standard and not idiomatic. – gniourf_gniourf Mar 24 '18 at 08:32
  • [`seq`](https://en.wikipedia.org/wiki/Seq_%28Unix%29) is part of coreutils and likely to exist on GNU systems, but is not part of POSIX and is not included with some other unix variants. Removed the reference. – Yann Vernier Mar 24 '18 at 08:35
2

Since this part of the question isn't cleared yet, and not fine to post in a comment, I add this partial answer:

x=1; for i in 1 2 3 ; do x=$x+2; echo $x; done 
1+2
1+2+2
1+2+2+2

As a side note: Don't use exit 0 at the end of your script without a good reason. When the script is done, it exits by itself without your help. The exit status will be the exit status of the last command performed, in your case a simple echo, which will almost always succeed. In the rare cases it fails, you will probably without intention hide that failure.

If you source the script, the exit will throw you out of your running shell.

But you can rewrite your while loop like this:

x=0
while (($((x)) < 9)) 
do  
  echo x : $x
  x=$x+2
done
echo $((x))

x : 0
x : 0+2
x : 0+2+2
x : 0+2+2+2
x : 0+2+2+2+2
10
user unknown
  • 35,537
  • 11
  • 75
  • 121
  • Thanks for the info and the partial answer. – John Doe Mar 24 '18 at 08:53
  • So you mean to say the x=$x+2 is just string concatenation in bash, and not integer addition, as it would look. – John Doe Mar 24 '18 at 09:14
  • Depends on who is looking :). Arithmetic expressions look like this in Bash: ((x+2)). Note that you omit the $ inside of ((x+2)). An `echo "$((1+2+2+2))"` would give you the expected result in the end. I add another code snippet. – user unknown Mar 24 '18 at 09:22