Created
September 12, 2012 20:53
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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