2

I am trying to build some beginner scripts with bash. I want to find how many days left from today until the end of the year, using date program.

So I am using a variable, current_date=$(date +%j) to get the number of the day we have now.

If I echo $current_date I am getting a result of 024, as normally expected because we have 24th of January.

So 365-024 are the days left. And here comes my query. Doing the calculation, lets say calc 365-024 I am getting the value of 345, instead of 341.

What's wrong? It has something to do with the zero on the front (024) isn't it?

Mr T
  • 125

2 Answers2

2

What's wrong? It has something to do with the zero on the front (024) doesn't it?

Yes, it does. The leading 0 means that the number is interpreted as an Octal (base 8) number.

  • Octal 024 == Decimal 20.

To interpret the number as a decimal number use:

$ echo $((024))
20

For more information see how to suppress bash octal number interpretation? (to be interpreted as decimal) which specifically addresses this very issue when performing calculations with date.


Numerical Constants

A shell script interprets a number as decimal (base 10), unless that number has a special prefix or notation.

  • A number preceded by a 0 is octal (base 8).
  • A number preceded by 0x is hexadecimal (base 16).
  • A number with an embedded # evaluates as BASE#NUMBER (with range and notational restrictions).

Representation of numerical constants

#!/bin/bash
# numbers.sh: Representation of numbers in different bases.

# Decimal: the default
let "dec = 32"
echo "decimal number = $dec"             # 32
# Nothing out of the ordinary here.


# Octal: numbers preceded by '0' (zero)
let "oct = 032"
echo "octal number = $oct"               # 26
# Expresses result in decimal.
# --------- ------ -- -------


# Hexadecimal: numbers preceded by '0x' or '0X'
let "hex = 0x32"
echo "hexadecimal number = $hex"         # 50

echo $((0x9abc))                         # 39612
#     ^^      ^^   double-parentheses arithmetic expansion/evaluation
# Expresses result in decimal.



# Other bases: BASE#NUMBER
# BASE between 2 and 64.
# NUMBER must use symbols within the BASE range, see below.


let "bin = 2#111100111001101"
echo "binary number = $bin"              # 31181

let "b32 = 32#77"
echo "base-32 number = $b32"             # 231

let "b64 = 64#@_"
echo "base-64 number = $b64"             # 4031
# This notation only works for a limited range (2 - 64) of ASCII characters.
# 10 digits + 26 lowercase characters + 26 uppercase characters + @ + _


echo

echo $((36#zz)) $((2#10101010)) $((16#AF16)) $((53#1aA))
                                         # 1295 170 44822 3375


#  Important note:
#  --------------
#  Using a digit out of range of the specified base notation
#+ gives an error message.

let "bad_oct = 081"
# (Partial) error message output:
#  bad_oct = 081: value too great for base (error token is "081")
#              Octal numbers use only digits in the range 0 - 7.

exit $?   # Exit value = 1 (error)

# Thanks, Rich Bartell and Stephane Chazelas, for clarification.

Source Advanced Bash-Scripting Guide - Numerical Constants

DavidPostill
  • 156,873
1

Writing numbers with leading 0 is a convention for octal notation, just like prefixing with 0x is for hexadecimal. So 024 is interpreted like 2*8+4 == 20. See output of the command:

echo $(( 024 ))

Strip leading zeroes using code examples from @steeldriver's code in comment below.

nsilent22
  • 141
  • 1
    There shouldn't be any need to resort to sed here: either use date format %-j to eliminate the zero-padding at source, use an explicit base in the shell 10#$current_date, or remove the leading zero using parameter substitution ${current_date#0} – steeldriver Jan 24 '16 at 15:11