usage example: exp 5*2-1
#!/bin/sh
<<'NOTES'
handy shell function/alias that wraps bc
tested & working with bash, busybox ash
Michael Sanders 2025
usage example: exp 5*2-1
NOTES
exp() {
s=3 # optional scale
e=$(printf '%s' "$*" | tr -d '[:space:]') # compress
[ -z "$e" ] && { echo "usage: exp <expression>"; return 1; }
# short-circuit invalid characters
echo "$e" | grep -Eq '^[0-9+*/().^-]+$' || \
{ echo "invalid expression"; return 1; }
# evaluate with bc -l
r=$(echo "scale=$s; $e" | bc -l 2>/dev/null) \
&& [ -n "$r" ] && printf "%.*f\n" "$s" "$r" \
|| { echo "invalid expression"; return 1; }
}
# test cases: expression|expected output
tests="
1+2|3.000
7-4|3.000
5*6|30.000
8/2|4.000
1+2*3|7.000
10-2*3|4.000
10/2+4*3|17.000
10/2*3+5|20.000
(1+2)*3|9.000
(10-2)/4|2.000
100-(20+5)*2|50.000
5*(3+(2*2))|35.000
-3+2|-1.000
2*-5|-10.000
-(4+1)*2|-10.000
-(2*-5)|10.000
-(-3)|3.000
2*(3+4*5)-10/2|41.000
(1+2)*(3+4)|21.000
((2+3)*4)-5|15.000
3*(2+(4*(1+1)))|30.000
((1+2)*3-(4/2))*5|35.000
0|0.000
0-0|0.000
10/3|3.333
3.5*2|7.000
-3.5+1.5|-2.000
1+2|3.000
7-4|3.000
5*6|30.000
8/2|4.000
2^3|8.000
3^4|81.000
(2+3)^2|25.000
4^(1+1)|16.000
-2^3|-8.000
1+2*foo|invalid expression
12+%|invalid expression
|usage: exp <expression>
"
# print CSV header
echo "Input|Expected|Actual|Pass"
IFS='
'
for t in $tests; do
str=$(echo "$t" | cut -d'|' -f1)
expected=$(echo "$t" | cut -d'|' -f2)
actual=$(exp "$str")
[ "$actual" = "$expected" ] && pass="YES" || pass="NO"
echo "$str|$expected|$actual|$pass"
done
# eof
On Fri, 31 Oct 2025 06:32:18 -0000 (UTC), Michael Sanders wrote:
usage example: exp 5*2-1
ldo@theon:~> bc <<<'5*2-1'
9
Dont need a whole script to save a few characters of typing ...
Your script looks quite complicated and hard to read. Quite some shell >commands used just ask for replacement (say, by Awk for example) to
make that simpler and (re: Subject) more "handy".
With GNU Awk (and co-process support) it might get yet even simpler.
ldo@theon:~> bc <<<'5*2-1'
9
DonrCOt need a whole script to save a few characters of typing ...
# short-circuit invalid characters
echo "$e" | grep -Eq '^[0-9+*/().^-]+$' || \
{ echo "invalid expression"; return 1; }
# evaluate with bc -l
r=$(echo "scale=$s; $e" | bc -l 2>/dev/null) \
ldo@theon:~> bc <<<'5*2-1'
9
DonrCOt need a whole script to save a few characters of typing ...
Anyway, I just use the following little script, called "iPerl":
--- Cut Here ---
#!/usr/bin/perl print ":-) ";
while (<>) { print eval; print ( ($@ || "\n") . ":-) " ) }
--- Cut Here ---
Note that you can't do this directly in AWK, b/c AWK (*) doesn't have
"eval".
(*) Yes, not even TAWK has eval.
Note also that you can wrap the above script in something like "rlwrap"
(or similar) to get keyboard scrolling, which is nice to have.
Your script looks quite complicated and hard to read. Quite some shell commands used just ask for replacement (say, by Awk for example) to make
that simpler and (re: Subject) more "handy".
With GNU Awk (and co-process support) it might get yet even simpler.
Unfortunately I cannot test that since my 'bc' strangely doesn't react
on scale=3 but the idea is something along the lines of [untested; may require tweaks]...
awk '
BEGIN { FS = OFS = "|" ; bc = "bc -ql" }
{ print $1 |& bc }
$2 { bc |& getline res
print $0, res, (res == $2) ? "YES" : "NO" }
'
with input like
scale=3|
1+2|3.000 7-4|3.000 ...
(Note that the scale should be coupled with the data, not hard-coded, if
you want to compare exact strings and not just the numeric values.)
Just an idea.
Janis
Why use -l if you disallow the functions it includes?
In article <10e2029$cc2l$1@dont-email.me>,
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
Your script looks quite complicated and hard to read. Quite some shell >>commands used just ask for replacement (say, by Awk for example) to
make that simpler and (re: Subject) more "handy".
With GNU Awk (and co-process support) it might get yet even simpler.
A couple of other things to note:
1) One problem with Mike's general approach, which I think we've all
hit at some point in our respective shell journeys, is that you end up
running and exiting 'bc' each time you invoke the
macro/alias/function/whatever.
A couple of other things to note:
1) One problem with Mike's general approach, which I think we've all
hit at some point in our respective shell journeys, is that you end up
running and exiting 'bc' each time you invoke the
macro/alias/function/whatever. You thus lose any context you may have
built up in 'bc' - starting from scratch each time. So, ...
Before there was 'fltexpr' (see below), in one of my scripts that
needed floating point math, I constructed a system where 'bc' was run
as a bash co-process. This worked well, since 'bc' gets run just once,
saving on process startup/exit cycles and also preserving context.
2) bash now has a loadable module called 'fltexpr' that evaluates
floating point expressions - much like $(( ... )) already does with
integer expressions. I haven't used this loadable much, because the
need just hasn't arisen, but the concept looks interesting (and long
overdue).
BTW, I understand that this loadable isn't needed in ksh, 'cause ksh
already does floating point. I never understood why bash didn't follow
ksh's lead and do likewise (until very recently...)
handy shell function/alias that wraps bc tested & working with bash,
busybox ash
# test cases: expression|expected output
Oh man, that takes me back to my old parsers. I just tried writing one
for Bash. It's really just a bare-bones demo though - everything's
integers, the numbers can only have a single digit, and the power
operator only works if both sides are between 0 and 11.
[...]
Lawrence DrCOOliveiro wrote:
ldo@theon:~> bc <<<'5*2-1'
9
DonrCOt need a whole script to save a few characters of typing ...
That's *more* typing than just using bc interactively.
On Fri, 31 Oct 2025 14:56:02 -0000 (UTC), Christian Weisgerber wrote:
Why use -l if you disallow the functions it includes?
Its not 'all there' yet. Make it better & post your results.
On 2025-10-31, Michael Sanders wrote:
On Fri, 31 Oct 2025 14:56:02 -0000 (UTC), Christian Weisgerber wrote:
Why use -l if you disallow the functions it includes?
Its not 'all there' yet. Make it better & post your results.
Why? The potential issue has already been written by Christian in
English and we can discuss it in the programming/design stage. No need
to write code (coding) to discuss this or to point out an issue.
(Also, is there even much to write code-wise, given it's, as far as I
can tell, about removing "-l" from the option list?)
(If you're not here to discuss the code, why did you post the code?)
#!/bin/sh
<<'NOTES'
handy shell function/alias that wraps bc
tested & working with bash, busybox ash
Michael Sanders 2025
usage example: exp 5*2-1
NOTES
exp() {
s=3 # optional scale
e=$(printf '%s' "$*" | tr -d '[:space:]') # compress
[ -z "$e" ] && { echo "usage: exp <expression>"; return 1; }
# short-circuit invalid characters
echo "$e" | grep -Eq '^[0-9+*/().^-]+$' || \
{ echo "invalid expression"; return 1; }
# evaluate with bc -l
r=$(echo "scale=$s; $e" | bc -l 2>/dev/null) \
&& [ -n "$r" ] && printf "%.*f\n" "$s" "$r" \
|| { echo "invalid expression"; return 1; }
}
It's not clear why you'd use bc instead of awk to evaluate the
expression, e.g. using any POSIX awk:
On Sat, 1 Nov 2025 11:47:47 -0500, Ed Morton wrote:
It's not clear why you'd use bc instead of awk to evaluate the
expression, e.g. using any POSIX awk:
I never bothered with Awk. I realized early on that Perl did everything
that Awk could do, and a lot more, just as concisely.
On Sat, 1 Nov 2025 11:47:47 -0500, Ed Morton wrote:
It's not clear why you'd use bc instead of awk to evaluate the
expression, e.g. using any POSIX awk:
I never bothered with Awk. I realized early on that Perl did everything
that Awk could do, and a lot more, just as concisely.
On 11/1/2025 3:21 PM, Lawrence DrCOOliveiro wrote:
On Sat, 1 Nov 2025 11:47:47 -0500, Ed Morton wrote:
It's not clear why you'd use bc instead of awk to evaluate the
expression, e.g. using any POSIX awk:
I never bothered with Awk. I realized early on that Perl did everything
that Awk could do, and a lot more, just as concisely.
Conciseness is brevity plus clarity and perl scripts are invariably
missing one of those qualities.
On Sun, 2 Nov 2025 16:58:54 -0600, Ed Morton wrote:
On 11/1/2025 3:21 PM, Lawrence DOliveiro wrote:
On Sat, 1 Nov 2025 11:47:47 -0500, Ed Morton wrote:
It's not clear why you'd use bc instead of awk to evaluate the
expression, e.g. using any POSIX awk:
I never bothered with Awk. I realized early on that Perl did everything
that Awk could do, and a lot more, just as concisely.
Conciseness is brevity plus clarity and perl scripts are invariably
missing one of those qualities.
Nope. Things like perl -lne or even perl -lnep allow you to write
short one-liners that are just as powerful as anything in Awk.
On 31.10.2025 13:39, Kenny McCormack wrote:
A couple of other things to note:
1) One problem with Mike's general approach, which I think we've all
hit at some point in our respective shell journeys, is that you end up >> running and exiting 'bc' each time you invoke the
macro/alias/function/whatever. You thus lose any context you may have >> built up in 'bc' - starting from scratch each time. So, ...
Before there was 'fltexpr' (see below), in one of my scripts that
needed floating point math, I constructed a system where 'bc' was run >> as a bash co-process. This worked well, since 'bc' gets run just once, >> saving on process startup/exit cycles and also preserving context.
Yep. For such "external services" co-processes are a fine feature. Interestingly, AFAIR, in Ksh, they were supported already with the
old 1988 version. In that light, Bash added that rather late.
There was just one caveat with co-processes; some external commands
do not use line-buffering and then, depending on tools' behavior, the co-process might not work correctly (without some pty feature added).
2) bash now has a loadable module called 'fltexpr' that evaluates
floating point expressions - much like $(( ... )) already does with
integer expressions. I haven't used this loadable much, because the
need just hasn't arisen, but the concept looks interesting (and long
overdue).
You mean in Bash we need (or needed?) two different methods for int
and float arithmetic respectively...?
BTW, I understand that this loadable isn't needed in ksh, 'cause ksh
already does floating point. I never understood why bash didn't follow >> ksh's lead and do likewise (until very recently...)
...or does that mean it is that now both supported with $((...)) ?
On 31/10/2025 17:14, Janis Papanagnou wrote:
On 31.10.2025 13:39, Kenny McCormack wrote:
BTW, I understand that this loadable isn't needed in ksh, 'cause
ksh
already does floating point. I never understood why bash didn't
follow
ksh's lead and do likewise (until very recently...)
...or does that mean it is that now both supported with $((...)) ?
In ksh:
$ echo $((4*atan(1)))
3.14159265358979324
In bash:
$ echo $((4*atan(1)))
bash: 4*atan(1): syntax error in expression (error token is "(1)")
Everything, it seems to me, is just easier with ksh.
On 03.11.2025 15:03, Richard Harnden wrote:
On 31/10/2025 17:14, Janis Papanagnou wrote:
On 31.10.2025 13:39, Kenny McCormack wrote:
BTW, I understand that this loadable isn't needed in ksh, 'cause >>>> ksh
already does floating point. I never understood why bash didn't >>>> follow
ksh's lead and do likewise (until very recently...)
...or does that mean it is that now both supported with $((...)) ?
In ksh:
$ echo $((4*atan(1)))
3.14159265358979324
In bash:
$ echo $((4*atan(1)))
bash: 4*atan(1): syntax error in expression (error token is "(1)")
Is that the "very recent" version of Bash that Kenny was speaking
about? - I read his post that Bash would *now* support what Ksh
supported for long.
Everything, it seems to me, is just easier with ksh.
Most things, indeed. (That's why I'm using Ksh since about 1990.)
Is that the "very recent" version of Bash that Kenny was speaking
about? - I read his post that Bash would *now* support what Ksh
supported for long.
No, that was 5.1.8. fltexpr needs at least 5.3.
[...]
It is all kind of a bodge and it would have been better if it could have
just been integrated into $(( ... )), like in ksh. [...]
In bash:
$ echo $((4*atan(1)))
bash: 4*atan(1): syntax error in expression (error token is "(1)")
Is that the "very recent" version of Bash that Kenny was speaking
about? - I read his post that Bash would *now* support what Ksh
supported for long.
On 03.11.2025 17:11, Kenny McCormack wrote:
[...]
It is all kind of a bodge and it would have been better if it could have
just been integrated into $(( ... )), like in ksh. [...]
Indeed. Especially if the lib is just adding FP (and nothing else).
Everything, it seems to me, is just easier with ksh.
On Mon, 3 Nov 2025 14:03:18 +0000, Richard Harnden wrote:
Everything, it seems to me, is just easier with ksh.
The saying rCLto someone with a hammer, every problem looks like a nailrCY comes to mind ...
On Mon, 3 Nov 2025 14:03:18 +0000, Richard Harnden wrote:
Everything, it seems to me, is just easier with ksh.
The saying rCLto someone with a hammer, every problem looks like a nailrCY
$ zsh -c 'echo $(( 1.0+2.3 ))'
3.2999999999999998
$ ksh -c 'echo $(( 1.0+2.3 ))'
3.3
Zsh is not really confidence-inspiring here. - Hmm..
On 03.11.2025 17:11, Kenny McCormack wrote:
[...]
It is all kind of a bodge and it would have been better if it could have
just been integrated into $(( ... )), like in ksh. [...]
Indeed. Especially if the lib is just adding FP (and nothing else).
Also with the paragon of Ksh or Zsh using already $((...)) for that.
I also cannot see an extension of $((...)) would break anything in
Bash, or would it?
Alas, re: Zsh, I just noticed...
$ zsh -c 'echo $(( 1.0+2.3 ))'
3.2999999999999998
$ ksh -c 'echo $(( 1.0+2.3 ))'
3.3
Zsh is not really confidence-inspiring here. - Hmm..
0.30.3
*print-flo-precision*15
(set *print-flo-precision* 17)17
0.30.29999999999999999
IEEE 64 bit floats can record a 15 digit number and reproduce
every digit.
On 2025-11-03, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
$ zsh -c 'echo $(( 1.0+2.3 ))'
3.2999999999999998
$ ksh -c 'echo $(( 1.0+2.3 ))'
3.3
Zsh is not really confidence-inspiring here. - Hmm..
I think 0.3 cannot be represented in binary floating point format,
so you are getting an approximation. Zsh and ksh appear to apply
different rounding on output. It's probably just the difference
between %.17g and %g or such.
On 04.11.2025 00:19, Christian Weisgerber wrote:
On 2025-11-03, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
$ zsh -c 'echo $(( 1.0+2.3 ))'
3.2999999999999998
$ ksh -c 'echo $(( 1.0+2.3 ))'
3.3
Zsh is not really confidence-inspiring here. - Hmm..
I think 0.3 cannot be represented in binary floating point format,
so you are getting an approximation. Zsh and ksh appear to apply
different rounding on output. It's probably just the difference
between %.17g and %g or such.
Yes, sure. - It's just that my "simple" pocket-calculator that
I bought around 1980 does not show such obvious rounding errors
(i.e. with numbers it can not exactly represent).
On 2025-11-04, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
On 04.11.2025 00:19, Christian Weisgerber wrote:
On 2025-11-03, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote: >>>
$ zsh -c 'echo $(( 1.0+2.3 ))'
3.2999999999999998
$ ksh -c 'echo $(( 1.0+2.3 ))'
3.3
Zsh is not really confidence-inspiring here. - Hmm..
I think 0.3 cannot be represented in binary floating point format,
so you are getting an approximation. Zsh and ksh appear to apply
different rounding on output. It's probably just the difference
between %.17g and %g or such.
Yes, sure. - It's just that my "simple" pocket-calculator that
I bought around 1980 does not show such obvious rounding errors
(i.e. with numbers it can not exactly represent).
Calculators use the same base for calculating that they do for
displaying and input. That makes all the difference.
You literally cannot input anything into your calculator that
it cannot represent. It supports an exact representation of 0.3,
or anything else you can punch in.
You can calculate something it cannot represent, like dividing 1 by 3.
But it does that in decimal and can truncate and round as you would
using pencil-and-paper calculations. If it agrees with pencil-and-paper
model decimal calculations, you will find it flawless. :)
On Sun, 2 Nov 2025 16:58:54 -0600, Ed Morton wrote:
On 11/1/2025 3:21 PM, Lawrence DrCOOliveiro wrote:
On Sat, 1 Nov 2025 11:47:47 -0500, Ed Morton wrote:
It's not clear why you'd use bc instead of awk to evaluate the
expression, e.g. using any POSIX awk:
I never bothered with Awk. I realized early on that Perl did everything
that Awk could do, and a lot more, just as concisely.
Conciseness is brevity plus clarity and perl scripts are invariably
missing one of those qualities.
Nope. Things like rCLperl -lnerCY or even rCLperl -lneprCY allow you to write short one-liners that are just as powerful as anything in Awk.
On Sun, 2 Nov 2025 16:58:54 -0600, Ed Morton wrote:
On 11/1/2025 3:21 PM, Lawrence DrCOOliveiro wrote:
On Sat, 1 Nov 2025 11:47:47 -0500, Ed Morton wrote:
It's not clear why you'd use bc instead of awk to evaluate the
expression, e.g. using any POSIX awk:
I never bothered with Awk. I realized early on that Perl did everything
that Awk could do, and a lot more, just as concisely.
Conciseness is brevity plus clarity and perl scripts are invariably
missing one of those qualities.
Nope. Things like rCLperl -lnerCY or even rCLperl -lneprCY allow you to write
short one-liners that are just as powerful as anything in Awk.
(flow "lnep" perm tprint)lnep
On 11/2/2025 7:53 PM, Lawrence DrCOOliveiro wrote:
On Sun, 2 Nov 2025 16:58:54 -0600, Ed Morton wrote:
On 11/1/2025 3:21 PM, Lawrence DrCOOliveiro wrote:
On Sat, 1 Nov 2025 11:47:47 -0500, Ed Morton wrote:
It's not clear why you'd use bc instead of awk to evaluate the
expression, e.g. using any POSIX awk:
I never bothered with Awk. I realized early on that Perl did
everything that Awk could do, and a lot more, just as concisely.
Conciseness is brevity plus clarity and perl scripts are invariably
missing one of those qualities.
Nope. Things like rCLperl -lnerCY or even rCLperl -lneprCY allow you to write
short one-liners that are just as powerful as anything in Awk.
Standard perl proponent type of response there.
On Fri, 31 Oct 2025 06:32:18 -0000 (UTC), Michael Sanders wrote:
usage example: exp 5*2-1
ldo@theon:~> bc <<<'5*2-1'
9
DonrCOt need a whole script to save a few characters of typing ...
On 2025-10-31 at 03:38 ADT, Lawrence DrCOOliveiro <ldo@nz.invalid> wrote:
On Fri, 31 Oct 2025 06:32:18 -0000 (UTC), Michael Sanders wrote:
usage example: exp 5*2-1
ldo@theon:~> bc <<<'5*2-1'
9
DonrCOt need a whole script to save a few characters of typing ...
Or in zsh, to avoid having to type quotes
In .zshrc put
aliases[==]='noglob bcfn'
bcfn () {
echo "scale=6 ; $*" | bc
}
At the prompt type
$ == 5*2+1
11
$
This definition
bcfn() { bc <<<"scale=6; $*"; }
was accepted, but then
bcfn 5*2+1
produced the error
zsh: no matches found: 5*2+1
(Did print the answer rCL11rCY in bash, but probably unsafe there too.)
The issue here is that 5*2+1 is interpreted as a glob to be expanded.
On Fri, 7 Nov 2025 10:09:57 -0400, Jim wrote:
On 2025-10-31 at 03:38 ADT, Lawrence DrCOOliveiro <ldo@nz.invalid> wrote: >>>
On Fri, 31 Oct 2025 06:32:18 -0000 (UTC), Michael Sanders wrote:
usage example: exp 5*2-1
ldo@theon:~> bc <<<'5*2-1'
9
DonrCOt need a whole script to save a few characters of typing ...
Or in zsh, to avoid having to type quotes
In .zshrc put
aliases[==]='noglob bcfn'
bcfn () {
echo "scale=6 ; $*" | bc
}
At the prompt type
$ == 5*2+1
11
$
Interesting: this
==() { bc <<<"scale=6; $*"; }
is directly valid in bash, but not zsh.
This definition
bcfn() { bc <<<"scale=6; $*"; }
was accepted, but then
bcfn 5*2+1
produced the error
zsh: no matches found: 5*2+1
(Did print the answer rCL11rCY in bash, but probably unsafe there too.)
...I will admit to being surprised that bash doesn't try
to glob the argument(s)...
Jim Diamond wrote:
...I will admit to being surprised that bash doesn't try to glob
the argument(s)...
It does. But upon failure, bash follows the (dangerous) precedent, established by sh, of passing globs that don't match anything as
literals. The danger in this is that, if you get in the habit of
relying upon the behaviour, you might find it matching something
when you didn't expect it.
Jim Diamond wrote:
...I will admit to being surprised that bash doesn't try
to glob the argument(s)...
It does. But upon failure, bash follows the (dangerous) precedent, established by sh, of passing globs that don't match anything as
literals. The danger in this is that, if you get in the habit of
relying upon the behaviour, you might find it matching something when
you didn't expect it.
I much prefer zsh's (default) behaviour of complaining. I can quote the thing, or use noglob if needed.
| Sysop: | Amessyroom |
|---|---|
| Location: | Fayetteville, NC |
| Users: | 54 |
| Nodes: | 6 (0 / 6) |
| Uptime: | 17:34:55 |
| Calls: | 742 |
| Files: | 1,218 |
| D/L today: |
4 files (8,203K bytes) |
| Messages: | 184,412 |
| Posted today: | 1 |