Есть ли определенный синтаксис для использования при использовании дробей или десятичных знаков в скрипте 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
.
Да, 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
.
Я укажу, что, хотя 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
, то предложения, которые действительно используют внешние команды, являются подходящим вариантом.
Сам Bash не может поддерживать числа с плавающей запятой, но есть программа bc, которая может выполнять десятичную арифметику.
blockquote>Ваш скрипт должен быть переписан для использования BC (aka Best Calculator) или другой другой утилиты . Итак, как вы можете сделать это ? Вы не можете использовать цикл
for
, так как встроенная команда bash не поддерживает плавающие точки. Либо вы используете другую переменную для управления внутри цикла и используете1
(или0
) в качестве отправной точки.
#!/bin/bash
{
n=9;
echo "$n";
for i in `seq .5 10`;
do
c=`bc <<< $i+1`;
echo $i "+" $c; done;
}
Есть способ подделать дроби, немного неловко, но в некоторых случаях его можно использовать:
#!/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 вы также можете сделать некоторые изящные (читай: неловкие) вычисления.
Большинство оболочек, включая 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 kbd>, то обычно это помещается в верхнюю часть скрипта (и запускает тот же исполняемый файл 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 .)
С вашим сценарием все в порядке, но вы можете упростить его синтаксис для улучшения читабельности:
{
и }
. ;
является избыточным в конце строки. ((
))
, $i
можно просто написать i
. ((
))
вместо let
. Без ведущего $
, он оценивает без расширения до результата. Не все оболочки поддерживают эту форму без расширения, но Баш и Зш (и некоторые другие) делают. Оставив немного о 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}
). Вопрос просит "использовать части/десятичные числа в сценарии удара?".
Предложения, которые "направляют вокруг проблемы... не используя удар", НЕ отвечают на вопрос.
Следующие удары демонстрационное десятичное число:
# 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 -----------------