Могу ли я использовать дробные / десятичные числа в скрипте bash?

Есть ли определенный синтаксис для использования при использовании дробей или десятичных знаков в скрипте bash?

Я пытался использовать следующий скрипт (в Ubuntu 12.04):

#!/bin/bash
{
n=9
echo "$n"
for (( i=.5; $i <10; i++ ));
      do
      let "c=$i+1"
      echo $i "+" $c
done
}

Это работает с i=1, но он генерирует синтаксическую ошибку, когда я помещаю в .5.

5
задан 29 September 2017 в 00:49

7 ответов

Да, bash обрабатывает только целые числа, но вы можете обойти проблему, либо не используя bash (Python очень прост в использовании), либо используя bc для обработки вычислений.

Помните, что ваше условие в цикле for не будет работать с дробными значениями, поэтому вам также необходимо его расширить.

step="0.5"

for (( i=0; $i<$(bc<<<"10/$step"); i++ )); do
      echo $(bc<<<"$step * $i")
done

Это будет эхо от 0 до 9.5 с шагом 0.5.

0
ответ дан 29 September 2017 в 00:49

Я укажу, что, хотя bash ограничивается целочисленной математикой, ksh93 нет. Так что этот (более простой пример) работает нормально:

$ for ((i=0.5; i<3; i=$((i+1)))); do print $i ; done
0.5
1.5
2.5

Мы используем i=$((i+1)), а не i++, потому что ++ работает только с целыми числами.

Так что я думаю, что это то, чего хочет первоначальный спрашивающий (за исключением, конечно, того, что это не делается с bash):

$ for ((i=0.5; i<10; i=$((i+1)))); do c=$((i+1)) ; print $i + $c ; done
0.5 + 1.5
1.5 + 2.5
2.5 + 3.5
3.5 + 4.5
4.5 + 5.5
5.5 + 6.5
6.5 + 7.5
7.5 + 8.5
8.5 + 9.5
9.5 + 10.5

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

0
ответ дан 29 September 2017 в 00:49

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

Ваш скрипт должен быть переписан для использования BC (aka Best Calculator) или другой другой утилиты . Итак, как вы можете сделать это ? Вы не можете использовать цикл for, так как встроенная команда bash не поддерживает плавающие точки. Либо вы используете другую переменную для управления внутри цикла и используете 1 (или 0) в качестве отправной точки.

0
ответ дан 29 September 2017 в 00:49
#!/bin/bash
{ 
n=9; 
echo "$n"; 
for i in `seq .5 10`;       
do       
c=`bc <<< $i+1`;       
echo $i "+" $c; done; 
}
0
ответ дан 29 September 2017 в 00:49

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

#!/bin/bash
{
n=9
echo "$n"

for (( i=5; $i <100; i+=10 ))
do
    let "c=$i+10"
    echo "$(( $i / 10 )).$(( $i % 10 )) + $(( $c / 10 )).$(( $c % 10 ))"
done
}

С помощью div / modulo вы также можете сделать некоторые изящные (читай: неловкие) вычисления.

0
ответ дан 29 September 2017 в 00:49

Вы можете использовать Zsh .

Большинство оболочек, включая Bash, напрямую не поддерживают вычисления с плавающей запятой и , как объяснил Оли , типичным обходным решением является вызов bc . Тем не менее, у вас есть ряд других опций .

Одним из них является использование Z Shell . zsh - это оболочка в стиле Борна ( того же «семейства» , что и Bash), которая напрямую поддерживает вычисления с плавающей запятой. Так что, если вы хотите сделать его скриптом Zsh вместо скрипта Bash, вы можете сделать это, изменив первую строку вашего скрипта с #!/bin/bash на #!/bin/zsh.

Как только вы это сделаете, это автоматически сработает! В этом случае дальнейших изменений в сценарии не требуется. (Иногда функция Bash и соответствующая функция Zsh работают по-разному, поэтому перевод сценария из Bash в Zsh требует внесения изменений. Хотя иногда сценарий Bash с ошибками в нем может на самом деле стать правильным, если его рассматривать как Скрипт Zsh, из-за различных проектных решений в Zsh о том, какие расширения должны выполняться автоматически.)

Это вывод:

9
0.5000000000 + 1.5000000000
1.5000000000 + 2.5000000000
2.5000000000 + 3.5000000000
3.5000000000 + 4.5000000000
4.5000000000 + 5.5000000000
5.5000000000 + 6.5000000000
6.5000000000 + 7.5000000000
7.5000000000 + 8.5000000000
8.5000000000 + 9.5000000000
9.5000000000 + 10.5000000000

Для работы вам понадобится Zsh. Zsh обычно не устанавливается в Ubuntu, но предоставляется пакетом zsh. Если у вас нет zsh, то вы увидите ошибку вроде:

-bash: ./your-script: /bin/zsh: bad interpreter: No such file or directory

В пакете Ubuntu zsh вы получите и /bin/zsh, и /usr/bin/zsh. Если вы хотите, чтобы ваш скрипт был переносимым на другие системы, в которых есть Zsh, но в других местах, вы можете вместо этого использовать строку hashbang вверху вашего скрипта:

#!/usr/bin/env zsh

Если пользователь может запустить Zsh, набрав zsh и нажав Enter , то обычно это помещается в верхнюю часть скрипта (и запускает тот же исполняемый файл zsh, который будет запускать пользователь). Это не будет работать на 100% систем - технически, env само по себе не должно быть предусмотрено в /usr/bin - но на практике это довольно редко для сбоя, при условии, что zsh находится в $PATH пользователя.

Для получения дополнительной информации об арифметической оценке в Zsh, включая арифметику с плавающей точкой, см. 11 Арифметическая оценка в Z Shell Manual [+1169]. [+1179]

Снижение точности вывода

Переменная с плавающей точкой x в Zsh может показывать большую точность, чем вы хотите, когда вы ее расширяете, например $x:

% ((x=.5))
% echo "$x"
0.5000000000

Один из способов расширять его без этих дополнительных нулей - вместо этого использовать арифметическое расширение:

% echo "$((x))"
0.5

Вы также можете использовать команду printf для отображения значения с ограниченной точностью:

% printf '%.2f\n' "$x"
0.50

Это работает и в других оболочках, даже если оболочка не поддерживает арифметику с плавающей точкой. (Некоторые оболочки не имеют встроенного printf, но вы все равно можете использовать эту команду, потому что автономно исполняемая версия установлена ​​в /usr/bin/printf.) Команда printf выиграла ' выполняет другую арифметику, кроме округления.

Даже в ситуациях, когда вам не нужно числовое форматирование, printf часто является лучшим выбором, чем echo в сценариях оболочки .

Если вам интересно, руководство Zsh (как указано выше) объясняет, как точно хранятся значения с плавающей запятой. (Обычно это IEEE 754 binary64 .)

Вы можете использовать более простой синтаксис (также в Bash).

С вашим сценарием все в порядке, но вы можете упростить его синтаксис для улучшения читабельности:

Оставив немного о n в начале (вам может понадобиться это для ваших целей, но я не могу сказать, для чего он), я предлагаю:

#!/bin/zsh

for ((i = .5; i < 10; i++))
do
    ((c = i + 1))
    echo "$i + $c"
done

Или , если вы не хотите, чтобы эти дополнительные нули были напечатаны:

#!/bin/zsh

for ((i = .5; i < 10; i++))
do
    echo "$((i)) + $((i + 1))"
done

Это печатает:

0.5 + 1.5
1.5 + 2.5
2.5 + 3.5
3.5 + 4.5
4.5 + 5.5
5.5 + 6.5
6.5 + 7.5
7.5 + 8.5
8.5 + 9.5
9.5 + 10.5

Наконец, для решения одной точки возможной путаницы:

  • Можно присвоить i с i = .5, потому что это было оценено как арифметическое выражение, из-за появления внутри (( )), и поэтому были разрешены пробелы вокруг =. Пробелы не требуются, и вам не нужно их использовать, но вы можете сделать это. (Я так и сделал.)
  • За пределами арифметического выражения для назначения переменной оболочки не должно быть пробелов по обе стороны от =. То есть обычно вы должны написать var=val, а не var = val. Аналогично, вне арифметического выражения, для доступа к значению переменной оболочки требуется $ (например, $i, ${i}).
0
ответ дан 29 September 2017 в 00:49

Вопрос просит "использовать части/десятичные числа в сценарии удара?".

Предложения, которые "направляют вокруг проблемы... не используя удар", НЕ отвечают на вопрос.

Следующие удары демонстрационное десятичное число:

# A crude bash trick for floats
# Bash does NOT do fractions/decimals. 
# Bash does integers (whole numbers).
# A simple loop can solve this though, not a big issue
# example whole number division shown below, results in float answer:

echo -n Enter number to divide:; read n
echo -n to be divided by?:; read div

mult=1      ;# to scale dividend, enabling higher precision quotient

# modify precision in both 'for' loops below,
# 2nd number only
# since they don't accept variables

for i in {1..6}; do mult=$mult"0"; done ;# precision at 6 decimals

result=$(echo -n $((n/div)).)       ;# division, with decimal point
temp=$((n%div*mult/div))        ;# numbers after decimal

echo $result$temp

# ----------------- to remove trailing zeros - OPTIONAL -----------------
# optionally, insignificant zeros may be removed
# many ways of doing this; I'm avoiding any new commands eg. awk, sed, head, tail
# check other topics to remove trailing zeros

#unset temp1

if [ $temp != 0 ]           ;# zero remainders to stay as a float
then

for i in {1..6}; do             # modify precision in both for loops

j=${temp: $((-0-$i)):1}         ;# find trailing zeros

if [ $j != 0 ]              ;# remove trailing zeros
then
    temp1=$temp1"$j"
fi
    done
else 
    temp1=0
fi

temp1=$(echo $temp1 | rev)
echo $result$temp1

# ----------------- OPTION CODE -----------------
0
ответ дан 23 November 2019 в 08:49

Другие вопросы по тегам:

Похожие вопросы: