5

I have the following code in a bash script, where "values" is a variable of newline separated numbers, some of which have leading 0's, and I am trying to iterate through each value in values and add each value to the variable "sum".

sum=0
while read line; do
    sum=$(( sum + line ))
done <<< "$values"

this code segment gives me the error: "value too great for base (error token is "09")", which as I understand, is because the bash arithmetic expression interprets the value "line" to be an octal value because it has a leading zero.

How can I allow for bash to interpret the value of line to be its decimal value? (e.g. 09 -> 9) for the value "line" within this bash arithmetic expression?

TheDarkHoarse
  • 416
  • 4
  • 9
  • @Rafael, ...why does that strike you as meaning the assumption is wrong? `02` is still 2, and `3` is decimal because it doesn't have any leading zero. Hence, you get `5` whether the `02` is octal *or* decimal. – Charles Duffy Oct 31 '18 at 02:00
  • @CharlesDuffy :facepalm: I can't believe I missed that – Rafael Oct 31 '18 at 02:04

3 Answers3

16

You can override the "leading 0 means octal" by explicitly forcing base ten with 10#:

sum=$(( 10#$sum + 10#$line ))

Note that, while you can usually leave the $ off variable references in arithmetic contexts, in this case you need it. Also, if the variable has leading spaces (in front of the first "0"), it won't parse correctly.

Gordon Davisson
  • 118,432
  • 16
  • 123
  • 151
2

To trim a single leading zero:

"${line#0}"

To trim any number of leading zeros:

"${line##+(0)}"

For example:

$ line=009900
$ echo "${line##+(0)}"
9900
l0b0
  • 55,365
  • 30
  • 138
  • 223
0

You can just get rid of the leading zeros, with something like:

shopt extglob on
x="${x##+(0)}"
[[ -z "${x}" ]] && x=0

This will remove all leading zeros and then catch the case where it was all zeros (leading to an empty string), restoring it to a single zero.

The following function (and test code) will show this in action:

#!/bin/bash

stripLeadingZeros() {
    shopt -s extglob
    retVal="${1##+(0)}"
    [[ -z "${retVal}" ]] && retVal=0
    echo -n "${retVal}"
}

for testdata in 1 2 3 4 5 0 09 009 00000009 hello 00hello ; do
    result="$(stripLeadingZeros ${testdata})"
    echo "${testdata} -> ${result}"
done

The output of that is:

1 -> 1
2 -> 2
3 -> 3
4 -> 4
5 -> 5
0 -> 0
09 -> 9
009 -> 9
00000009 -> 9
hello -> hello
00hello -> hello
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953