Результат с плавающей запятой в целочисленном делении по Bash
У меня есть скрипт резервного копирования на моем сервере, который выполняет задания резервного копирования cron и отправляет мне сводку файлов, для которых была создана резервная копия, включая размер нового файла резервной копии. В рамках сценария я хотел бы разделить окончательный размер файла на (1024^3), чтобы получить размер файла в ГБ, из размера файла в байтах.
Поскольку bash не имеет вычисления с плавающей запятой, я пытаюсь использовать каналы для bc, чтобы получить результат, однако я получаю тупик на основных примерах.
Я пытался получить значение Пи в масштабе, однако,
хотя работает следующее:
~ #bc -l
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
4/3
1.33333333333333333333
22/7
3.14285714285714285714
q
0
quit
Неинтерактивная версия не работает:
#echo $(( 22/7 )) | bc
3
Это работает:
#echo '22/7' | bc -l
3.14285714285714285714
Но мне нужно использовать переменные. Так что не помогает, что следующее не работает:
#a=22 ; b=7
#echo $(( a/b )) | bc -l
3
Я явно что-то упускаю в синтаксисе для использования переменных в Bash, и мог бы использовать с некоторыми "указателями" на то, что я неправильно понял.
Как сказал DigitalRoss, я могу использовать следующее:
#echo $a / $b | bc -l
3.14285714285714285714
Однако я не могу использовать сложные выражения, такие как:
#echo $a / (( $b-34 )) | bc -l
-bash: syntax error near unexpected token `('
#echo $a / (( b-34 )) | bc -l
-bash: syntax error near unexpected token `('
#echo $a / (( b-34 )) | bc -l
-bash: syntax error near unexpected token `('
Может кто-нибудь дать мне рабочий правильный синтаксис для получения результатов с плавающей запятой со сложными арифметическими выражениями?
6 ответов
Просто двойная кавычка ("
) выражение:
echo "$a / ( $b - 34 )" | bc -l
Тогда bash расширит $
переменные и игнорировать все остальное и bc
увидим выражение в скобках:
$ a=22
$ b=7
$ echo "$a / ( $b - 34 )"
22 / ( 7 - 34 )
$ echo "$a / ( $b - 34 )" | bc -l
-.81481481481481481481
Обратите внимание, что ваш echo $(( 22/7 )) | bc -l
фактически заставляет bash вычислять 22/7 и затем отправлять результат в bc. Таким образом, целочисленный вывод не является результатом bc, а просто вводится в bc.
Пытаться echo $(( 22/7 ))
без трубопровода до н.э., и вы увидите.
scale
переменная определяет количество цифр после десятичного разделителя
$ bc
$ scale=2
$ 3/4
$ .75
Я бы предпочел awk вместо bc, он делает то же самое в одной команде, а также дает вам больше гибкости для добавления переменных и форматирования вывода с помощью printf:
# Define vars in the command
awk -v a=3 -v b=2 'BEGIN{print a/b}'
1.5
# Define vars earlier and init with them awk vars
c=3
d=2
awk -v a=$c -v b=$d 'BEGIN{print a/b}'
1.5
# Use vars that are defined in script
a=3
b=2
awk 'BEGIN{print '$a'/'$b'}'
# Format your output using C printf syntax
awk -v a=3 -v b=2 'BEGIN{printf("%.3f\n", a/b)}'
1.500
Также bc не возвращает ошибку кода, если он делится на ноль, поэтому вы не можете проверить ошибку:
echo 3/0 | bc -l
Runtime error (func=(main), adr=5): Divide by zero
# The error code is zero, that means there is no errors
echo $?
0
Хотя awk возвращает ошибку кода 2:
awk -v a=3 -v b=0 'BEGIN{print a/b}'
awk: cmd. line:1: fatal: division by zero attempted
# awk returned code error 2, that indicates that something went wrong
echo $?
2
Ошибка кода может использоваться для проверки деления на ноль, например:
# Set your own vars
if output=$(awk -v a=3 -v b=0 'BEGIN{print a/b}' 2> /dev/null); then
echo "$output"
else
echo "error, division by zero"
fi
вы можете обрабатывать проверку ошибок div-zero непосредственно по адресу:
for a in 19 29 31; do for b in 11 3 0; do gawk -v PREC=512 -Mbe '$++NF= +(_=$NF) ? $(!!_)/_ : "div_by_zero"' \ \ CONVFMT='%.59g' OFS=' \t| ' <<< "${a} ${b}"; done; done
19 | 11 | 1.7272727272727272727272727272727272727272727272727272727273
19 | 3 | 6.3333333333333333333333333333333333333333333333333333333333
19 | 0 | div_by_zero
29 | 11 | 2.6363636363636363636363636363636363636363636363636363636364
29 | 3 | 9.6666666666666666666666666666666666666666666666666666666667
29 | 0 | div_by_zero
31 | 11 | 2.8181818181818181818181818181818181818181818181818181818182
31 | 3 | 10.333333333333333333333333333333333333333333333333333333333
31 | 0 | div_by_zero
если тебе все это не нужноGMP
точность, затем готов напрямую вернуть бесконечность вместо сообщения о фатальной ошибке:
for a in 19 29 31; do for b in 11 3 0; do mawk '$++NF=$++_/$(_+_--)' CONVFMT='%.19g' OFS='\t' <<<"$a $b";done;done
или еще лучше, сделайте это с одного вызова наawk
вместо того, чтобы называть это безостановочно:
for a in 19 29 31; do for b in 11 3 0; do echo "${a} ${b}" done; done | mawk '$++NF = $(++_) / $(_+_--)' CONVFMT='%.19g' OFS='\t'
19 11 1.727272727272727293
19 3 6.333333333333333037
19 0 inf
29 11 2.636363636363636243
29 3 9.666666666666666075
29 0 inf
31 11 2.818181818181818343
31 3 10.33333333333333393
31 0 inf
Или, если вы так предпочитаете, естьmawk
вызовgawk-gmp
косвенно:
echo "22 7\n22 4\n22 0" | mawk '$++NF = substr(_=__="", (__="gawk -v PREC=65536 -Mbe"\ " \47BEGIN { printf(\"%.127f\","(+(_=$(NF-!_))\ ? "("($!__)")/("(_)")" : (+(_=$!__)<-_?__:"-") \ "log(_<_)")") } \47" ) | getline _, close(__))_'
22 7 3.1428571428571428571428571428571428571428571428571428………
22 4 5.5000000000000000000000000000000000000000000000000000………
22 0 +inf
Чтобы подробнее рассказать об ответах от 22 февраля 2013 г. и 1 сентября 2022 г., используйтеecho
для этого тоже не требуется. Вы также можете использовать строки здесь (<<<) для ввода данных в файлы .
bc -l <<< "$a / ( $b - 34 )"
Иногда это полезно для написания быстрых острот.
user@machine:~$ a=22; b=7; bc -l <<< "$a / ( $b - 34 )"
-.81481481481481481481
scale
к этому можно очень легко добавить.
user@machine:~$ a=22; b=7; c=2; bc <<< "scale=$c; $a / ( $b - 34 )"
-.81
Эта строка взята изbash
и неbc
, поэтому это работает и со многими другими командами терминала.