Skip to content

Instantly share code, notes, and snippets.

@rsvp
Created September 12, 2012 20:53
Show Gist options
  • Save rsvp/3709847 to your computer and use it in GitHub Desktop.
Save rsvp/3709847 to your computer and use it in GitHub Desktop.
aw.sh : use awk as quick custom calculator on command line -- with easy syntax. / Linux bash script.
#!/usr/bin/env bash
# bash 4.2.24(1) Linux Ubuntu 12.04 Date : 2012-09-13
#
# _______________| aw : use awk as quick custom calculator on command line.
#
# Usage: aw 'expression' [decimal/sig:8] [format:g]
# aw bak # show log file of previous result.
#
#
# User-defined: r = random number strictly between 0 and 1,
# never 0 nor 1, see rand().
# s = stored full result from previous script call.
# pi, e.
# mil, bil, tri; pc, bp, milli, macro, nano.
# [ Also add your favorite functions. ]
# [ Recommended format choices are e, f, or g. ]
#
# Examples: % aw '(3+2)*r'
# 4.63147 # random real between 0 and 5.
# % aw r 0 f
# 1 # random integer, 0 or 1.
# % aw '10 * pc * 210'
# 21.0000 # 10% of 210
# % aw 's/2'
# 10.5000 # take half of previous result.
#
# Dependencies: gawk (on Ubuntu 12.04 prefer installing "gawk" package
# since inferior mawk of 1996 is the default awk.)
# This script will use system's awk if gawk is unavailable.
#
# Definitive REFERENCE by Arnold Robbins, "gawk: Effective AWK Programming",
# http://www.gnu.org/software/gawk/manual/ (2011)
#
# WARNING: Unassigned variables in awk are given value of 0.
# Thus do not misspell variable names!!
#
# Exponentiation: the character sequence ** is equivalent to ^.
# exp() is available; and the constant e is defined in the script.
# log() is the natural logarithm.
# int() is integer truncation.
# Functions: sqrt, sin, cos, and atan2 are also available in gawk.
# CHANGE LOG LATEST version available: https://bitbucket.org/rsvp/gists/src
# 2012-09-13 Add variable for awkprg, in case gawk is unavailable.
# printf modified for portability.
# Replace both head and tail by sed for portability.
# Memorialize first argument on memf's third line.
# 2012-09-10 Add printf format as third argument.
# Shadow the full result into storage.
# 2012-09-09 First version using gawk 3.1.8 under Ubuntu 12.04,
# script patterned on pye. gawk is 12% the size of Python.
# Fixed awk's seed for randomness problem.
# _____ PREAMBLE_v2: settings, variables, and error handling.
#
LC_ALL=POSIX
# locale means "ASCII, US English, no special rules,
# output per ISO and RFC standards."
# Esp. use ASCII encoding for glob and sorting characters.
shopt -s extglob
# ^set extended glob for pattern matching.
shopt -s failglob
# ^failed pattern matching signals error.
set -e
# ^errors checked: immediate exit if a command has non-zero status.
set -u
# ^unassigned variables shall be errors.
# Example of default VARIABLE ASSIGNMENT: arg1=${1:-'foo'}
ndec=${2:-'8'}
# ^default number of significant or decimal places.
# ndec=0 gives rounded integer result [unlike int()], but
# see printf section below for important rounding caveats.
letter=${3:-'g'}
# ^control printf format, e.g. e, f, g -- for
# exponent, float, or mixture with max significant digits.
program=${0##*/} # similar to using basename
# memf=$( mktemp /dev/shm/88_${program}_tmp.XXXXXXXXXX )
memf="/dev/shm/88_${program}_tmp"
# ^intended to persist, to save last result.
cleanup () {
# Delete temporary files, then optionally exit given status.
local status=${1:-'0'}
# rm -f $memf
[ $status = '-1' ] || exit $status # thus -1 prevents exit.
} #--------------------------------------------------------------------
warn () {
# Message with basename to stderr. Usage: warn "message"
echo -e "\n !! ${program}: $1 " >&2
} #--------------------------------------------------------------------
die () {
# Exit with status of most recent command or custom status, after
# cleanup and warn. Usage: command || die "message" [status]
local status=${2:-"$?"}
cleanup -1 && warn "$1" && exit $status
} #--------------------------------------------------------------------
trap "die 'SIG disruption, but cleanup finished.' 114" 1 2 3 15
# Cleanup after INTERRUPT: 1=SIGHUP, 2=SIGINT, 3=SIGQUIT, 15=SIGTERM
#
# _______________ :: BEGIN Script ::::::::::::::::::::::::::::::::::::::::
if ( which gawk > /dev/null ) ; then
awkprg='gawk'
else
# warn "gawk is not installed. Falling back on system's awk." #-DEBUG
awkprg='awk'
# Ubuntu 12.04 awk defaults to inferior mawk.
fi
# Retrieve result from previous execution:
if [ -s $memf ] ; then
stored="$( sed -n -e '2p' $memf )"
# ^pick out the second printf from gawk output.
if [ "$1" = 'bak' ] ; then
cat $memf
cleanup
fi
else
stored=0
# ^expected awk behavior.
fi
#-------------------------------------- awk at work...
{ $awkprg -f - <<EOHereDoc
BEGIN {
s = "$stored"
e = 2.718281828459045
pi = 3.141592653589793
mil = 1000000
bil = 1000000000
tri = 1000000000000
pc = 0.01
milli = 0.001
bp = 0.0001
macro = 0.000001
nano = 0.000000001
srand( $(date '+%N') )
# ^To SEED the random generator,
# awk by default uses time which does not change for 1 sec,
# thus executions during that second gives the same output.
# Our FIX is to use the nanoseconds portion of time
# which looks like 877928409 -- nine digits.
# Execution of aw one nanosecond apart generates, e.g.
# 0.5656
# 0.1706
# 0.2621
# 0.2180
# 0.0804
# 0.7866
# So it looks like the neighborhood problem is avoided.
}
function pch (x, y) {
# percentage change
return ( (y-x)/x )*100
}
# There is no middle section { }
# because that is for evalating each record.
# Of course, we have no records here!
END {
r = rand()
# ^ one-time convenient random number variable.
# It costs virtually nothing in execution time.
evala = ( $1 )
# ^ evaluated argument from command line.
printf "%.${ndec}${letter}\n", evala
#-X printf "%1\$.${ndec}${letter}\n", evala # OK on gawk, not on mawk.
# Floating-point with ndec number of decimal places.
# ___ATTN___ printf does NOT round the decimal portion.
# For ndec=0, we do not get integer truncation.
# Rounding is "unbiased," which means it doesn't always round
# a trailing '.5' up, contrary to naive expectations.
# In unbiased rounding, '.5' rounds to even, rather than always up,
# so 1.5 rounds to 2 but 4.5 rounds to 4. See
# http://www.gnu.org/software/gawk/manual/html_node/Round-Function.html
# which provide the usual rounding function.
#
# CONVFMT's default value is "%.6g", which yields a value
# with at least six significant digits.
printf "%.15e\n", evala
#-X printf "%1\$.15e\n", evala # OK on gawk, not on mawk.
# SECOND printf reports the FULL version for storage,
# since the first printf may not have fully expressed
# the complete result due to user formatting.
}
EOHereDoc
} > $memf
# Preserve TWO results into memory.
#-------------------------------------- awk done.
# Show just the requested format result on STDOUT,
# i.e. the first printf within awk.
sed -n -e '1p' $memf
#-X head -n1 $memf # replaced for portability.
echo "$1" >> $memf
# memorialize first argument on memf's third line.
cleanup
# _______________ EOS :: END of Script ::::::::::::::::::::::::::::::::::::::::
# vim: set fileencoding=utf-8 ff=unix tw=78 ai syn=sh :
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment