Краткое содержание: надо учитывать или явно указывать систему счисления.
Простая задача: найти, какой месяц был 7 месяцев назад.
Любым способом получаем текущий месяц, например:
(дальше использованы фрагменты рабочего скрипта, поэтому много излишеств, удалены некоторые другие команды, которые этими излишествами активно используются)
# лишняя переменная tSA только для удобства, чтобы не появились ошибки из-за
# неправильного экранирования вложенных команд
tSA=`date +%s`
# сегодня
todaySA=`date -d @$tSA +%Y%m%d`
# месяц сегодня
motSA=${todaySA:4:2}
Если месяц меньше 10, то в переменную motSA попадает значение вида 0N, где N - номер месяца с добавленным ведущим нулём. Всё красиво и гладко.Теперь находим искомое:
curmo=$motSA
echo curmo=$curmo
oldermo=$[curmo-7]
echo oldermo=$oldermo
Однако же... На дворе сентябрь, а значит curmo=09. По логике, 7 месяцев назад был февраль. Казалось бы, чего проще:
oldermo=$[curmo-7] ==> oldermo=$[09-7]
Однако, вместо oldermo=2 получаем ошибку:
$ curmo=09;echo $[curmo-7]
bash: 09: значение слишком велико для основания (ошибочная метка "09")
Мать моя женщина! Что за нафиг? Роемся в памяти и вспоминаем, что в некоторых языках программирования число с ведущим нулём считается заданным не в десятичной системе. В каком-то из ассемблеров ведущий ноль указывал на восьмеричную систему счисления (СС), и там вылезла бы подобная ошибка, потому как восьмеричные цифры от нуля до семёрки.
Начинаем курить мануал по bash и без проблем находим упоминание про base, hexadecimal, octal и т.п. В языке FORTH есть специальная конструкция, позволяющая временно изменить основание системы счисления (ОСС)в пределах текущего (под)выражения, что-то подобное должно быть и в bash. Точно, есть.
Для указания СС, в которой записано число, перед этим числом ставим префикс, в котором указываем десятичное значение ОСС. Например, 16 для шестнадцатиричной:
$ a=$[16#20];echo $a; a=$[8#20];echo $a;
32
16
По умолчанию bash распознает восьмеричную и шестнадцатеричную СС по префиксам 0 и 0x соответственно. Есть и некоторые другие особенности (знак "меньше" в сочетании "greater< than 9" не мой - скопировано из мануала к bash 4.3.11, для версии 4.2.25 такой ошибки нет):
Constants with a leading 0 are interpreted as octal numbers. A leading 0x or 0X denotes hexadecimal. Otherwise, numbers take the form [base#]n, where the optional base is a decimal number between 2 and 64 representing the arithmetic base, and n is a number in that base. If base# is omitted, then base 10 is used. When specifying n, the digits greater< than 9 are represented by the lowercase letters, the uppercase letters, @, and _, in that order. If base is less than or equal to 36, lowercase and uppercase letters may be used interchangeably to represent numbers between 10 and 35.
Ладно, с ОСС разобрались, тестовый пример работает:
$ echo $[10#09-7]
2
И тут снова вылезает злосчастное "однако":
$ curmo=09;echo $[10#curmo-7]
bash: 10#curmo: значение слишком велико для основания (ошибочная метка "10#curmo")
На этот раз ошибка в том, что с указанием ОСС имя переменной перестает восприниматься как имя, а считается просто числом, записанным в указанной СС. Но в десятичке нет цифр c, m, o, r или u. Придется разыменовывать переменную:
$ curmo=09;echo $[10#$curmo-7]
2
Ура!