Сравните десятичные дроби в Bash во время цикла

В приведенном ниже коде ShellCheck выдает ошибку в while пункт.

count=10.0
while [ $count -le 20.0 ]
do
    echo "Hello"
    count=$(bc<<< "scale=4; (count+0.1)")
done

ShellCheck говорит:

Десятичные числа не поддерживаются, используйте целые числа или bc

Я не совсем уверен, как использовать BC в while петля.

while [ $(bc <<< "scale=4; (count -le 20.0)" ]

Как сравнить десятичные числа в предложении while? Любой совет?

3 ответа

Решение

Bash не поддерживает арифметику с плавающей запятой. Вы можете использовать bc:

count="10.0"
limit="12.0"
increment="0.1"

while [ "$(bc <<< "$count < $limit")" == "1"  ]; do
    echo "Hello"
    count=$(bc <<< "$count+$increment")
done

или awk:

while awk 'BEGIN { if ('$count'>='$limit') {exit 1}}'; do
    echo "Hello"
    count=$(bc <<< "$count+$increment")
done

Мне просто интересно: почему бы (напрямую) не сосчитать с 10.0 до 12.0?

for i in $(seq 10.0 0.1 12.0); do
    echo "Hello"
done

Bash не поддерживает арифметику с плавающей точкой. Ты можешь использовать bc для этого сравнения тоже:

count=10.0

while : ;
do
    out=$(bc -l<<< "$count<20.0")
    [[ $out == 0 ]] && { echo "Reached limit" ; exit 0; }

    echo "Hello"
    count=$(bc<<< "scale=4; ($count+0.1)")
done

Обратите внимание, что я добавил недостающие $ в count внутри цикла, где вы обновляете count,

Хотя bash не обрабатывает числа с плавающей запятой, seq Утилита делает. [Примечание 1]

Основной синтаксис seq FIRST INCREMENT LASTтак что в вашем случае вы могли бы использовать

for count in "$(seq 10.0 0.1 20.0)"; do
  # something with $count
done

Если вы предоставляете два аргумента, предполагается, что они ПЕРВЫЙ и ПОСЛЕДНИЙ, с INCREMENT равным 1. Если вы предоставляете только один аргумент, он считается ЛАСТНЫМ, причем ПЕРВЫЙ и INCREMENT равны 1. Как в вашем примере, последовательность включительно, поэтому будут получены как FIRST, так и LAST при условии, что INCREMENT равномерно делит FIRST-LAST.

Вы также можете включить явный формат:

$ seq -f "%06.3f" 1 .5 2
01.000
01.500
02.000

Недостатком этого метода является то, что он предварительно вычисляет весь набор значений. Если цикл будет выполняться сотни тысяч раз, это может занять много памяти, и в этом случае вы могли бы использовать замену канала или процесса:

while read count; do
  # something with count
done < <(seq 10.0 0.000001 20.0)

Заметки

  1. seq не Posix, но он почти всегда присутствует; он входит в состав GNU coreutils, и аналогичная утилита, доступная в Mac OS X) работает в NetBSD начиная с 3.0, а FreeBSD - с 9.0.
Другие вопросы по тегам