63

When running this part of my bash script am getting an error

Script

value=0
for (( t=0; t <= 4; t++ ))
do
d1=${filedates[$t]}
d2=${filedates[$t+1]}
((diff_sec=d2-d1))
SEC=$diff_sec
compare=$((${SEC}/(60*60*24)))
value=$((value+compare))
done

Output

jad.sh: line 28: ((: 10#2014-01-09: value too great for base (error token is "09")
jad.sh: line 30: /(60*60*24): syntax error: operand expected (error token is "/(60*60*24)")

d1 and d2 are dates in that form 2014-01-09 and 2014-01-10

Any solution please?

codeforester
  • 39,467
  • 16
  • 112
  • 140
user3178889
  • 689
  • 1
  • 7
  • 8
  • 2
    You can't just subtract dates in the form YYYY-MM-DD. You have to convert them to plain numbers first, like time_t timestamps (which will get you seconds). – Mark Reed Jan 10 '14 at 16:47
  • 4
    Looks like it's converting your 09 to octal notation, so chances are it's actually trying to compute `2014 - 1 - 9`, but since `09` is not a valid number (the 0 at the front means use octal instead of decimal) it's complaining. – robbrit Jan 10 '14 at 16:49
  • 1
    what the solution robbirt? – user3178889 Jan 10 '14 at 16:59

7 Answers7

80

Prepend the string "10#" to the front of your variables. That forces bash to treat them as decimal, even though the leading zero would normally make them octal.

rojomoke
  • 3,765
  • 2
  • 21
  • 30
20

What are d1 and d2? Are they dates or seconds?

Generally, this error occurs if you are trying to do arithmetic with numbers containing a zero-prefix e.g. 09.

Example:

$ echo $((09+1))
-bash: 09: value too great for base (error token is "09")

In order to perform arithmetic with 0-prefixed numbers you need to tell bash to use base-10 by specifying 10#:

$ echo $((10#09+1))
10
dogbane
  • 266,786
  • 75
  • 396
  • 414
10

As others have said, the error results from Bash interpreting digit sequences with leading zeros as octal numbers. If you have control over the process creating the date values and you're using date, you can prefix the output format string with a hyphen to remove leading zero padding.

Without prefixing date format with hyphen:

$ (( $(date --date='9:00' +%H) < 10 )) && echo true || echo oops
-bash: ((: 09: value too great for base (error token is "09")
oops

With prefixing date format with hyphen:

$ (( $(date --date='9:00' +%-H) < 10 )) && echo true || echo oops
true

From the date man page:

By default, date pads numeric fields with zeroes. The following optional flags may follow '%':

  -      (hyphen) do not pad the field
enharmonic
  • 1,800
  • 15
  • 30
3

d1 and d2 are dates in that form 2014-01-09 and 2014-01-10

and then

((diff_sec=d2-d1))

What do you expect to get? ((diffsec=2014-01-09-2014-01-10)) ??

You need to convert the dates to seconds first:

d1=$( date -d "${filedates[$t]}" +%s )
d2=$( date -d "${filedates[$t+1]}" +%s )
(( compare = (d2 - d1) / (60*60*24) ))
(( value += compare ))
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
1

Posting some tips here related to the title of this question, but not directly related to the details of the original question. I realize that's a bit controversial action on Stack Overflow, however these related questions:

convert octal to decimal in bash [duplicate]

Value too great for base (error token is "08") [duplicate]

point to this one, and yet they are closed and hence, I could not post this answer there. Therefore, this seemed like a logical place (at least to me) to post this information that may help others in a similar situation, especially new-to-BaSH programmers.

An alternative approach to ensuring a number is treated as a 10-base integer is to use printf. This command instructs printf to treat $num as an integer and round it to 0 decimal places.

num="$(printf "%.0f" "$num")"

Or, if you want to also ensure there are no non-numeric characters in the string, you can do this:

num="$(printf "%.0f" "${num//[!0-9]/}")"

Both commands will strip out leading zeroes and round decimal values to the nearest whole number. Note the first (simpler) solution works with negative numbers, but the second does not (it will always return absolute value).

Note that printf rounds down, meaning .01 to 0.5 is rounded down to 0, while .51 to .99 is rounded up to 1. Basically, the difference between rounding up versus down in this case is that printf rounds down 0.5 and any below. I mention this because 0.5 rounded up is a more common practice.

Now, addressing the OP's specific scenario.... Combining printf with awk allows arithmetic expressions not possible with printf alone.

This

compare=$((${SEC}/(606024)))

could be alternatively be expressed as

compare=$(awk -v sec=$SEC 'BEGIN { print int(sec/(60*60*24))}')

or

compare="$(printf "%.0f" "$(awk "BEGIN { print ( $SEC / ( 60 * 60 * 24 ) ) }")")"

Meanwhile,

value=$((value+compare))

Could be calculated as

value="$(printf "%.0f" "$(awk "BEGIN { print ( $value + $compare ) }")")"
MrPotatoHead
  • 1,035
  • 14
  • 11
0

You don't need the $ and the {} in an arithmetic expansion expression. It should look like this:

compare=$((SEC/(60*60*24)))
hek2mgl
  • 152,036
  • 28
  • 249
  • 266
  • 1
    That solves the second error. still the first error "value too great for base .." – user3178889 Jan 10 '14 at 16:58
  • 2
    As @robbrit says, remove the leading zero from `09` or better fix the code which produces the `09`. Otherwise it is treated as an octal number. (Or check dogbane's answer) – hek2mgl Jan 10 '14 at 17:03
0

For 'mm' and 'dd' values in dates, I use this trick:

mm="1${date:5,2}"  # where 5 is the offset to mm in the date
let mm=$mm-100     # turn 108 into 8, and 109 into 9
Dick Guertin
  • 747
  • 8
  • 9