Wykonywanie prostej matematyki w wierszu poleceń za pomocą funkcji bash: 1 $ podzielony przez 2 $ (być może używając bc)


33

Czasami muszę podzielić jedną liczbę na drugą. Byłoby wspaniale, gdybym mógł po prostu zdefiniować funkcję bash. Do tej pory jestem zmuszony używać takich wyrażeń jak

echo 'scale=25;65320/670' | bc

ale byłoby świetnie, gdybym mógł zdefiniować funkcję .bashrc, która wyglądałaby

divide () {
  bc -d $1 / $2
}

Odpowiedzi:


45

Mam przydatną funkcję bash o nazwie calc:

calc () {
    bc -l <<< "$@"
}

Przykładowe użycie:

$ calc 65320/670
97.49253731343283582089

$ calc 65320*670
43764400

Możesz to zmienić według własnego uznania. Na przykład:

divide() {
    bc -l <<< "$1/$2"
}

Uwaga: <<< jest tutaj ciągiem, który jest podawany na standardowe wejście bc. Nie musisz wywoływać echo.


3
Zgadzam się, że wygląda lepiej bez dodatkowego echa, ale ponieważ jest to wbudowana powłoka, nie wymaga żadnych dodatkowych kosztów. To, co powstrzymuje mnie od używania „<<<” w funkcjach powłoki, to jej ograniczenie do bash, podczas gdy dodatkowe echo pozwala na użycie go w oryginalnej powłoce bourne lub ksh bez zmian.
Tatjana Heuser

@TatjanaHeuser ksh, wariant 93, obsługuje ciągi tutaj.
Kusalananda

Aby przypisać do zmiennej, zrobiłem to: foo = $ (bc -l <<< "(2 * 5) + 5")
thebiggestlebowski 12.02.18

20

Bash potrafi do pewnego stopnia matematykę. Jednak nie jest przydatna ze względu na dokładność, ponieważ zaokrągla.

[user]$ echo $(( 10/5 ))
2

Ale masz rację - funkcja bash byłaby prostym skrótem, a twój przykład w zasadzie działa.

divide() {
  echo "scale=25;$1/$2" | bc
}

Rzuć to w swoim .bashrc, a następnie możesz:

[user]$ divide 10 5
2.0000000000000000000000000

1
echo $((10 / 5))nie wymaga cytatu.
użytkownik nieznany

1
Znacząca irytacja: echo $((6383/7671)) da ci zero. Musisz wyraźnie mówić o liczbach zmiennoprzecinkowych:echo $((6383.0/7671.0))
Sridhar Sarnobat

9

Prawdopodobnie znasz bash wbudowany 'expr' jak w

$ expr 60 / 5
12

który jest ograniczony do liczb całkowitych i wymaga spacji między argumentami.

Co powstrzymuje Cię przed zdefiniowaniem funkcji na podstawie wyrażenia echa, którego już używasz? To znaczy

 divide () {
   echo $1/$2 | bc
 }

1
po prostu ignorancja. nie zdawałem sobie sprawy, że mogę sprawić, żeby działało w ten sposób ...
ixtmixilix

7

Nie jest to właściwie odpowiedź na to dokładne pytanie, ale warto wiedzieć. Użyj zsh;-)

% echo $((65320./670))
97.492537313432834

Właściwie szukałem tego pytania, ponieważ zmieniłem na zsh i nie mogłem korzystać z exprwbudowanego bash , więc mimo to dobra odpowiedź :)
Steen Schütt

2

Jeśli calczainstalowałeś w swoim systemie i nie lubisz zaokrąglania, możesz:

div() { calc "$1 / $2"; }

hmm ... nie mogłem znaleźć żadnego pakietu Debiana dla 'calc'
ixtmixilix 31.01

Pakiet nazywa się apcalc (Arbitary Precision) ... Plik binarny nazywa sięcalc
Peter.O

@ Peter.O Ok, w repozytoriach Arch Linux nazywa się calc.

2

Brudny hack dla małych wartości i ograniczonej precyzji bez użycia bc polegałby na pomnożeniu nominatora przed podziałem, aby uzyskać dokładny wynik.

Przykład bez precyzji:

echo $((13/7)) 
1

z dokładnością do 2 cyfr: pomnóż przez 100 i przesuń kropkę dziesiętną o 2 kroki w lewo:

echo $((100*13/7)) | sed 's/..$/.&/'
1.85
echo $((100*13/7))%
185%

Jest to przydatne tylko wtedy, gdy znany jest zakres liczb, a dokładność jest zawsze taka sama. Unikanie dzwonienia do bc i dzwonienie do sed nie wydaje się zbyt rozsądne.

Pamiętaj, że pomnożenie wartości może prowadzić do błędów przepełnienia, ale niezbyt wcześnie:

echo $((1000000000*12345678901))
-6101065172709551616

Używanie podwójnych nawiasów do oceny wyrażeń matematycznych jest przestarzałe. Użyj: $[expression]zamiast
laebshade

3
@laebshade: Mogę udowodnić, że się mylisz, prawda jest odwrotna. Oto cytat podręcznika bash:The old format $[expression] is deprecated and will be removed in upcoming versions of bash.
nieznany użytkownik

Omówiono także tutaj ,  tutaj  i  tutaj .
G-Man mówi „Reinstate Monica”

0

Zamiast używać bc, możesz użyć awk:

div() { awk -v p=$1 -v q=$2 'BEGIN{print p/q}'; }

-4
echo Division
efgh=$num1/$num2
remainder=$num1%num2
echo $num1 "/" $num2 "=" $efgh.$remainder
echo
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.