Арифметическая синтаксическая ошибка только в alpine linux

Я действительно новичок в bash и только сегодня начал писать небольшой планировщик. После тестирования на Mac и Ubuntu все работало нормально. Но когда я хотел выполнить его в альпийском Linux, выдается ошибка: арифметическая синтаксическая ошибка. Я не могу понять, почему это так, и не смог найти решение в дяде Google. Я надеюсь, что кто-то может мне помочь.

Это выражение:

[ $((10#$(date +"%H%M") != 10#$START_TIME)) = 1 ]

В контексте, показывающем, как это выполняется:

#!/bin/bash

START_TIME=2100
END_TIME=2200

#  arithmetic syntax error
while [ $((10#$(date +"%H%M") != 10#$START_TIME)) = 1 ]; do 
    sleep 10;
done

person vitalragaz    schedule 09.01.2018    source источник
comment
Вы можете воспроизвести это за пределами Alpine — просто нужно протестировать везде, где sh реализуется ash или dash.   -  person Charles Duffy    schedule 09.01.2018
comment
Кстати, я предполагаю, что вы запускаете sh scriptname? Поскольку у него есть шебанг #!/bin/bash, это указывает на то, что он был написан для работы только в системах с установленным bash и для использования bash в качестве интерпретатора. Alpine не поставляет bash — у него есть меньшие по размеру и более быстрые для запуска оболочки, такие как dash.   -  person Charles Duffy    schedule 09.01.2018
comment
@CharlesDuffy .. А? Отметил ваш комментарий The # isn't treated as a comment when it's in the middle of a word для моего теперь удаленного ответа.. О боже, о боже. Спасибо за это понимание. :)   -  person sjsam    schedule 09.01.2018


Ответы (1)


Этот код использует синтаксис, не указанный в POSIX, чтобы гарантировать, что значения анализируются как десятичные числа, даже если они начинаются с ведущих нулей.

Простой ответ: «не делайте этого», то есть либо используйте сравнения строк, а не числовые, либо обрежьте начальные 0 по-другому, если вам нужно работать с интерпретаторами baseline-POSIX.

Придерживайтесь числового маршрута: например, неэффективно использовать sed для задания, но это работает:

START_TIME=2100 # this already doesn't start with a 0
while [ $(( $(date +"%H%M" | sed -e 's/^0+//') != START_TIME )) = 1 ]; do
  sleep 10
done

Или, используя сравнение строк напрямую:

START_TIME=2100 # exactly four digits
while [ "$(date +"%H%M")" != "$START_TIME" ]; do
  sleep 10
done
person Charles Duffy    schedule 09.01.2018
comment
Спасибо за ваш быстрый ответ, как бы вы предпочли, чтобы скрипт выглядел наиболее эффективно? - person vitalragaz; 09.01.2018
comment
Если вы хотите оптимизировать эффективность времени выполнения после запуска, я бы установил bash вместо использования /bin/sh - таким образом вы могли бы заменить date на printf '%(%H%M)T' и вообще не иметь никаких подоболочек, вызывающих внешние процессы. - person Charles Duffy; 09.01.2018
comment
(имейте в виду, выполнение этого без подоболочек означало бы отдельное присваивание, т.е. while printf -v current_time '%(%H%M)T' && [[ $current_time != $START_TIME ]]; do -- обратите внимание, что я переключаюсь с числового сравнения на строковое; нет причин для числового сравнения для точной проверки равенства). - person Charles Duffy; 09.01.2018
comment
может быть, вы можете помочь мне в последний раз: while printf -v current_day '%(%A)T' && [[ ${DAYS,,} != *"${current_day,,}"* ]] || printf -v current_time '%(%H%M)T' && [[ $current_time != $STOP_TIME ]]; do -> ошибка, она говорит мне: printf: команда не найдена, я уверен, что допустил ошибку в условии: / Просто попытался соединить эти условия вместе, но я не могу разберись как должно быть правильно что бы работало :S Хочу добиться чтобы скрипт тоже мог запускаться только в определенные дни недели в определенные часы. - person vitalragaz; 09.01.2018
comment
Если я правильно понимаю, что вы хотите сгруппировать каждый printf с последующим тестом, это может выглядеть примерно так: while { printf -v current_day '%(%A)T' -1 && [[ ${DAYS,,} != *"${current_day,,}"* ]]; } || { printf -v current_time '%(%H%M)T' -1 && [[ $current_time != $STOP_TIME ]]; }; do. Кстати, DAYS, CURRENT_TIME и STOP_TIME, возможно, DAYS, CURRENT_TIME и STOP_TIME - все заглавные; см. pubs.opengroup.org/onlinepubs/9699919799/basedefs/, четвертый абзац: имена, написанные заглавными буквами, используются для переменных, имеющих значение для ОС или оболочки; другие имена зарезервированы для использования приложением. - person Charles Duffy; 09.01.2018
comment
(Значки -1 означают текущее время; shellcheck.net выдает предупреждения без них, хотя printf в любом случае действует правильно) . - person Charles Duffy; 09.01.2018
comment
... во всяком случае, если у вас возникнут дополнительные проблемы после этого подхода, я бы предложил новый вопрос. - person Charles Duffy; 09.01.2018
comment
Почему бы просто не использовать здесь сравнение строк: [ "$(date +%H%M)" != $START_TIME ]? В худшем случае для этого требуется, чтобы START_TIME было 4-значным временем с ведущими нулями, но это кажется разумным требованием. - person chepner; 09.01.2018
comment
@chepner, да, я сам прибыл туда раньше (хотя никогда не включал это в ответ) - прочитайте историю комментариев. Однако это стоит включить; делать это сейчас. - person Charles Duffy; 09.01.2018