![]() |
|
|||||||
| Guides All Guides and HOWTO's. |
![]() |
|
|
Thread Tools | Display Modes |
|
|
|
|||
|
A simple human front-end for 'bc(1)' the unlimited precision calculator.
Some weeks ago I got tired of dealing with inhumanely formatted big numbers like those presented by fdisk(1) of a 320 GB hard disk Code:
Disk: wd1 geometry: 38913/255/63 [625142448 Sectors]
Offset: 0 Signature: 0xAA55
Starting Ending LBA Info:
#: id C H S - C H S [ start: size ]
-------------------------------------------------------------------------------
0: A5 0 1 1 - 5220 254 63 [ 63: 83875302 ] FreeBSD
*1: A6 5221 0 1 - 10441 254 63 [ 83875365: 83875365 ] OpenBSD
2: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused
3: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused
To solve this issue I wrote a simple shell script with pre-processes input for 'bc(1)', and post-processes it's output. According to the man page, 'bc' is an arbitrary-precision arithmetic language and calculator. The 'bc' language supports variables, loops and functions. My goal was to only support simple single line calculations with Code:
- + : unary minus and plus + - * / % : addition, subtraction, multiplication, division and modulus The number format expected by 'bc' does not allow the usage of comma's or periods to group the digits in groups of three. Only a period '.' is allowed to specify a decimal fraction. The shell script 'calc' supports two number formats: 12,345,678.99, as used in the Anglo-Saxon world, and 12.345.678,99 as used in other parts of the world. The 12,345,678.99 I will call 'commified' and 12.345.678,99 'periodified'. Conversion of commified input for digestion by 'bc' is simple, just remove all comma's. A 'periodified' number needs two steps: first remove all periods '.', then change the lonely ',' into a '.' Output conversion Conversion of the result spit out by 'bc' is not that easy. The 'Perl cookbook' by Christiansen and presents a 'commify' perl script. My commented version of this script Code:
#!/usr/bin/perl -w
# $Id: commify,v 1.1 2009/02/22 02:31:30 j65nko Exp $
use strict ;
# Input style nr without ",'s : -155566.1234
# Output style : -1,555,666.1234
# taken from 'Perl Cookbook'
# (\d\d\d)(?=/d) : group of 3 digits followed by another digit
# (?!\d*\.) : the digits should not be followed by a decimal "."
# -155566.1234 reversed : 4321.6665551-
# ^^^^ group of 3 digits followed by another digit is candidate for
# insertion of ','. Because it is followed by a decimal point, it is a decimal fraction,
# and decimal fraction shouldn't be grouped. So the '(?!\d*\.)' makes sure this does not
# happen
# The first group of 3 digits is 666, which are followed by a '5'.
# The regex engine just looks ahead for the 5, but will not consume it.
# In the next iteration, it will start at this '5' and thus find '555' followed by the '1'.
# So a ',' will be inserted.
# The next iteration starting at the '1' finds a '-' , no other 2 digits for
#
sub commify {
my $nr = reverse $_[0] ;
$nr =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g ;
return scalar reverse $nr ;
}
while (<>) {
print commify $_ ;
}
This script also formed the inspiration of the 'periodify' script: Code:
#!/usr/bin/perl -w
#$Id: periodify,v 1.1 2009/02/22 02:32:51 j65nko Exp $
use strict ;
# inspired by 'commify' routine from 'Perl Cookbook'
# first eliminate all ',' : 1,444,444.99 -> 1444444.99
# replace decimal "." by decimal ',' : 1444444.99 -> 1444444,99
# adds periods to group digits : 1.444.444,99
sub periodify {
my $nr = reverse $_[0] ;
$nr =~ s/,//g ;
$nr =~ s/\./,/ ;
$nr =~ s/(\d\d\d)(?=\d)(?!\d*,)/$1./g ;
return scalar reverse $nr ;
}
while (<>) {
print periodify $_ ;
}
Code:
#!/bin/sh
# $Id: calc,v 1.2 2009/02/22 02:38:06 j65nko Exp $
case "$1" in
#
-p ) # clean 1.000.000,99 -> 1000000.99 as required by 'bc'
CLEAN="sed -e s!\.!!g -e s!,!\.!g"
FORMAT='periodify' # answer produced by 'bc'
# replace decimal point by ',' and reinsert '.' for grouping
;;
-c ) # clean 1,000,000.99 -> 1000000.99 as required by 'bc'
CLEAN="sed -e s!,!!g"
FORMAT='commify' # answer produced by 'bc'
;;
*) echo "Usage: $0 -p (group digits with periods: '.')"
echo " or"
echo " $0 -c (group digits with commas : ',')"
exit 1
esac
while true ; do
printf "Expression : " >&2
read EXPR
if [ -z "${EXPR}" ] ; then break
fi
[ -t 0 ] || echo "${EXPR}"
EXPR=$( echo "${EXPR}" | $CLEAN )
#printf "Normalized : $EXPR\n"
ANSWER=$(echo "scale=4 ; ${EXPR}" | bc )
HUMAN=$(echo "${ANSWER}" | ${FORMAT} )
printf "%s ( %s )\n" ${ANSWER} ${HUMAN}
done
__________________
You don't need to be a genius to debug a pf.conf firewall ruleset, you just need the guts to run tcpdump |
|
|||
|
To use a simpler version, called kal that only does a single calculation without formatting the numbers, you can use the following in your .profile:
Code:
# $OpenBSD: dot.profile,v 1.4 2005/02/16 06:56:57 matthieu Exp $
#
# sh/ksh initialization
PATH=$HOME/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/X11R6/bin:/usr/local/bin:/usr/local/sbin:/usr/games:.
export PATH HOME TERM
export PAGER=/usr/bin/less
export PS1="[\u@\h]\w: "
kal(){ echo "scale=4 ; $@" | bc ;}
To stimulate the neurons in your gr[ae]y matter, an uncommented illustration of something you can run into: Code:
$ ls -l 2013* -rw-r--r-- 1 j65nko j65nko 0 Feb 2 19:35 2013-02-01_data24 -rw-r--r-- 1 j65nko j65nko 0 Feb 2 19:40 20134343 $ kal 2013*3 20134343 $ kal 2013\*3 6039 $ kal 2013*4 bc: stdin:1: syntax error: _data24 unexpected $ kal '2013*4' 8052
__________________
You don't need to be a genius to debug a pf.conf firewall ruleset, you just need the guts to run tcpdump |
![]() |
| Tags |
| arithmetic, bc(1), calculate, calculator |
| Thread Tools | |
| Display Modes | |
|
|
Similar Threads
|
||||
| Thread | Thread Starter | Forum | Replies | Last Post |
| A simple question | Mr-Biscuit | Off-Topic | 1 | 16th April 2009 04:26 PM |
| Simple Firewall with PF | jones | FreeBSD General | 3 | 7th November 2008 01:02 AM |
| Help With [seemingly] Simple Problem | MustLearn | FreeBSD General | 3 | 7th October 2008 10:05 AM |
| Simple/easy ircd | Weaseal | FreeBSD Ports and Packages | 0 | 17th July 2008 12:31 PM |
| RPN Calculator?? | DrJ | FreeBSD General | 7 | 30th May 2008 01:16 AM |