Результат с плавающей запятой в целочисленном делении по 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, поэтому это работает и со многими другими командами терминала.

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