0

I've just started programming shell script on my school and I've come over some problems I can't find answers on.

I have a school assignment to make a shell script where a user are supposed to type a number + enter and repeat this until the user types ctrl+d, then the program should echo the sum and the program should end.

This is the script I wrote.

sum=0
while [ true ]
do
    read number
    sum=$(($sum+$number))
done

function finish {
    echo "Sum: $sum"
}

trap finish exit

Running the script in my terminal it looks like this:

4
5
summer.sh: line 5: 9+: syntax error: operand expected (error token is "+")
Sum: 9

I read How can I add numbers in a bash script and thought I used the arithmetic expression correct (it even displays the right sum), but nevertheless I tried another suggestion from the same question. I changed it to:

sum=$((sum+number))

Now I don't get the syntax error. But I can't terminate the program by typing ctrl-d. Example of running code now:

4
5
^D
^C

What happened? Why does a change in the arithmetic expression in the script affect the behaviour of ctrl-d?

Community
  • 1
  • 1
user3065567
  • 71
  • 1
  • 1
  • 3

2 Answers2

0

There are a couple of problems in your script:

You never check the result of read. Ctrl+D will make read terminate with an error code of 1, so you should check that:

read number || break

Otherwise, BASH will never exit the loop.

Then the trap should be at the top of the script (which means you must move the function as well). BASH reads the script line by line. It doesn't look ahead. So your very complicated code (function upto exit) is exactly the same as:

echo "Sum: $sum"

Moving the function to the top should work despite the fact that $sum isn't defined at the time when the function is parsed because BASH will read the body of the function again when it is invoked. At that time, sum should exist and have the correct value.

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
  • The positioning of the `trap` is a good point but in this case doesn't actually matter. Since nothing above the `trap` is supposed to exit the script it will be executed before the script exits under normal operations. The main issue here is the `read` return issue. The first script exits because of the numeric context error. The second does not because unset variables evaluate to 0 in numeric contexts (but explicit variable expansions apparently expand to an empty string when empty which is something I didn't know). – Etan Reisner Sep 10 '14 at 09:51
  • Thanks! I will try to fix it! Reisner: "The first script exists because of the numeric context error" No, it exits when ctrl+d is typed. I can run the program with many more inputs and it doesn't exit before I type ctrl-d. EDIT: Adding || break fixed it! :) – user3065567 Sep 10 '14 at 11:23
  • @user3065567 Yes, when you hit ctrl-d you don't give read any data so your variable is empty which causes the parsing error in the numeric context which causes the script to exit. Switch to implicit variable expansion in the numeric context (drop the `$`) and the error goes away because the "empty" variables are now expanded to `0` so you get a valid numeric expression, no error, and a loop that never ends. I'm probably going to expand this explanation into an answer so that it is more fully explained and demonstrated. – Etan Reisner Sep 10 '14 at 12:21
  • 1
    @user3065567: Or to put it another way: Just typing ENTER without any number should have the same effect as Ctrl+D. – Aaron Digulla Sep 10 '14 at 12:23
0

This script should illustrate the problem that the shell was running into as well as emulating what the fixed version of the script does.

#!/bin/bash

sum=0
while true
do
    read -p 'Number? ' number
    echo "number:$number:"
    echo "\$(($sum+$number))"
    echo "\$(($sum+${number:-0}))"
done

$ ./explain.sh
Number? 5
number:5:
$((0+5))
$((0+5))
Number? 3
number:3:
$((0+3))
$((0+3))
# At this prompt I just hit enter
Number?
number::
$((0+))
$((0+0))
# At this prompt I hit ctrl-d
Number? number::
$((0+))
$((0+0))

The replacement by zero for an undefined variable is explained in the man page:

Shell variables are allowed as operands; parameter expansion is performed before the expression is evaluated. Within an expression, shell variables may also be referenced by name without using the parameter expansion syntax. A shell variable that is null or unset evaluates to 0 when referenced by name without using the parameter expansion syntax.

Etan Reisner
  • 77,877
  • 8
  • 106
  • 148