1

I'm trying to write a loop that computes operations in the order they come (not the usual mathematical precedence) The code goes like this (echo is for debugging):

while [[ "$(echo "$newstring"| grep -E ^-?[0-9]+$)" = "" ]]; do
       oldpart="$(echo "$newstring"|cut -f1-3 -d' ')"
       echo "bla $oldpart"
       newpart="$(echo "$oldpart"|bc)"
       echo "ble $newpart"
       newstring="$(echo "$newstring"|sed -e "s/$oldpart/$newpart/")"
       echo "bli $newstring"
done

Output is the following when passed $newstring as "6 + 6 * 9":

6 + 6 * 9
bla 6 + 6
ble 12
bli 12 * 9
bla 12 * 9
ble 108
bli 12 * 9
bla 12 * 9
ble 108

As we can see, 6 + 6 is calculated as 12 as expected, then replaced in the string. The operation then restarts for 12 * 9, 108 - which fails to replace into the string... and the while never ends

I suspect sed interprets the * which might prevent the desired substition.

Any idea how to bypass that behaviour?

3
  • Yes, * is part of basic regular expressions and thus is interpreted. Better to change the approach. Are elements always separated by space in the input expression, or is 1+1 valid? Are only elementary operations supported or should it compute log(sin(4))?
    – Quasímodo
    Commented Dec 18, 2020 at 10:39
  • 1
    To "compute operations in the order they come" RPN is a natural choice. The tool is dc. Your example translates to echo "6 6 + 9 * p" | dc Commented Dec 18, 2020 at 10:47
  • space can be removed without issues I use them to cut at this moment, the only operations used are + and *. <br> dc approach seemss interesting indeed, but it seems more complicated to automatically generate the calling strings for larger operations. Commented Dec 18, 2020 at 11:26

1 Answer 1

3

A solution using just bash with no external commands needed

#!/bin/bash
newstring='6 + 6 * 9'

read -a atoms <<<"$newstring"
run=${atoms[0]}               # Initialise running total to the first value

for ((i=1; i<=${#atoms[@]}; i+=2))
do
    op=${atoms[$i]}           # Next operator
    num=${atoms[$((i+1))]}    # Next number
    run=$((run $op num))      # Perform the arithmetic (integer maths)
done
echo "$run"

If you want to use floating arithmetic you will need to use either bc or dc. This variant uses dc, as suggested in a comment

#!/bin/bash
newstring='6.5 + 6 * 9'

{
    read -a atoms <<<"$newstring"

    run=${atoms[0]}
    printf "%s " "$run"

    for ((i=1; i<=${#atoms[@]}; i+=2))
    do
        op=${atoms[$i]} num=${atoms[$((i+1))]}
        printf " %s %s" "$num" "$op"
    done
    printf " p\n"
} | dc
1
  • Very creative, didn't think about doing it as an array! I am using the first part thanks a lot Commented Dec 18, 2020 at 15:54

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .