0

I'm writing what should be a simple bash script to calculate the minimum value p needs to be in the formula C=1-(p^n). C and p are input by the user and are floating point numbers.

For example if C=0.36 and p=0.8, then my program should return 2 for the value of n, since 1-(0.8^2)=0.36

My code is below. Please note: I have put |bc in many different places in my while loop condition statement and each time I get an error. The last place I tried was, as you can see, between the two closing brackets.

I receive the following error:

operand expected (error token is ".36")

#!/bash/bin

echo "Enter I/O fraction time:"
read ioINPUT
echo "Enter expect CPU utilization:"
read uINPUT
n=1

function cpuUTIL {
    x='scale=3; 1-$ioINPUT**$n'|bc
}

while [[ cpuUTIL -lt $uINPUT ]|bc]; do
    n+=1
    x='scale=3; 1-$ioINPUT**$n'|bc
done
echo $c

How do I properly use bc in a bash script while loop condition statement?

3
  • A few problems with the code: 1. /bash/bin spoonerism. 2. Var names should be c, p, and n, not $ioINPUT, $uINPUT, and $x. 3. Last line is echo $c, yet $c is never assigned. 4. Tests an integer-only -lt comparison with a decimal value. 5. Function cpuUTIL can't be tested with -lt. 6. Variable assignments produce no output, so piping them to bc has no effect. 7. ]|bc] typo. 8. The while test wouldn't be affected by what's in that loop, and if started must run forever. Commented Mar 27, 2017 at 4:18
  • Thanks for the tips. 1 and 3 occurred because I was rushing and am tired. I apologize. Only problem I don't understand is 2: why should my variables be named c, p, and n? Commented Mar 27, 2017 at 4:37
  • Since it's a simple program and the formula being modeled uses those variables, then it's simpler for the program to follow suit. If the code must have long names, then it'd be better to use the names of whatever attributes those equation letters are modeling. Commented Mar 27, 2017 at 9:16

3 Answers 3

3

A few issues have been pointed out in comments. Here is a fixed up version of what you were trying:

#!/bin/bash

cpuUTIL () {
    bc <<< "scale = 3; 1 - $ioINPUT ^ $n"
}

read -p "Enter I/O fraction time: " ioINPUT
read -p "Enter expect CPU utilization: " uINPUT
n=1

while (( $(bc <<< "$(cpuUTIL) < $uINPUT") )); do
    (( ++n ))
    x=$(bc <<< "scale = 3; 1 - $ioINPUT ^ $n")
done

echo $x

Comments:

  • #!/bin/bash, not #!/bash/bin
  • echo and read can be shortened to read -p
  • function cpuUTIL is less portable than cpuUTIL ()
  • Single quotes prevent parameter expansion; you have to use double quotes for your bc commands
  • I've used here-strings (<<<) instead of pipes to avoid creating a subshell
  • "Power of" in bc is ^ and not **
  • The while condition is complex: is uses nested command substitution within (( )), which translates the output of the bc statement (1 if the comparison holds, 0 if it doesn't) to true/false for Bash
  • n+=1 appends the string 1 to the contents of variable n (unless it was declared as an integer with declare -i, but that's rather exotic), so you have to use something like an arithmetic context, (( ++n ))
  • The final statement should probably be echo $x

But, as demonstrated by karakfa's answer, you can do it directly. In bc, it would look like this:

$ p=0.8
$ C=0.36
$ bc -l <<< "scale = 3; l(1-$C) / l($p)"
2.000

bc -l defines the math library, which contains (among a few others) the natural logarithm l(x).

Sign up to request clarification or add additional context in comments.

2 Comments

Since bc has variables, it can be done entirely in bc: bc -l <<< "p=0.8 ; c=0.36 ; scale = 3 ; l(1-c) / l(p)"
@agc, Sure, but the user should still be prompted to provide the values instead of using two magic constants, so I assumed this happens outside of bc, and the bc command is part of a shell script. You could of course also prompt the user from bc with read() (if you use GNU bc) and make the whole thing a bc executable.
1

you can iterate and find a value, but why? here is a better method

awk -v p=0.8 -v C=0.36 'BEGIN{print log(1-C)/log(p)}'

also the value will be, in general floating point, you can get the floor by wrapping the log ratio with int()

1 Comment

I initially wanted to use logarithms, but couldn't figure out how to implement them with bc, so I chose to iterate. I just started reading about awk after I posted this question (and gave up on bc). Thank you for the help.
1

Using GNU bc:

#!/usr/bin/bc -ql
scale=3
print "Enter I/O fraction time: "
p=read()
print "Enter expected CPU utilization: "
c=read()
l(1-c)/l(p)
quit

Save the above to foo.bc, run chmod +x foo.bc, then execute with ./foo.bc.

Or without prompts, from bash:

bc -l <<< "p=0.8;c=0.36;scale=3;l(1-c)/l(p)"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.