Skip to content

Commit

Permalink
Much improved code, various bug fixes (relative temp convertion).
Browse files Browse the repository at this point in the history
Drop support for the toggling feature.
  • Loading branch information
mountaineerbr committed Dec 22, 2024
1 parent 07c965f commit c62496b
Showing 1 changed file with 91 additions and 122 deletions.
213 changes: 91 additions & 122 deletions ctemp.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
# Convert amongst temperature units
# v0.6 sep/2024 by mountaineerbr
# v0.7 dec/2024 by mountaineerbr

#defaults

Expand All @@ -10,9 +10,6 @@ SCALEDEF=2
#script name
SN="${0##*/}"

#make sure locale is set correctly
export LC_NUMERIC=C

#help
HELP="$SN - Convert amongst temperature units
Expand All @@ -27,11 +24,6 @@ SYNOPSIS
degrees Celsius. TEMP is a floating point number and can be a
simple arithmetic expression.
If no unit is given, toggling between Celsius and Fahrenheit
units will be activated for absolute temperature conversions.
Toggling creates a temporary file at /tmp or equivalent. Input
from stdin is supported.
Option -r deals with relative temperatures conversions; for
example, a change of 45 degrees Fahrenheit corresponds to a
change of 25 degrees Celsius.
Expand All @@ -40,16 +32,7 @@ SYNOPSIS
Floating point results are rounded to scale and trailing zeroes
are trimmed.
Temperature unit conversions are nonlinear; for example, temper-
ature conversions between Fahrenheit and Celsius scales cannot
be done by simply multiplying by conversion factors. These equa-
tions can only be used to convert from one specific temperature
to another specific temperature; for example, you can show that
the specific temperature of 0.0 Celsius equals 32 Fahrenheit, or
that the specific temperature of 100 Celsius equals 212 Fahrenheit.
However, a temperature difference of 100 degrees in the Celsius
scale is the same as a temperature difference of 180 degrees in
the Fahrenheit scale.
Input from stdin is supported.
FORMULAS
Expand All @@ -64,8 +47,8 @@ FORMULAS
0K = -273.15 C
Equivalence of relative temperature differences.
45K = 25 C
25C = 45 K
45F = 25 C
25K = 45 F
25C = 25 K
Expand All @@ -79,7 +62,7 @@ SEE ALSO
Multiplications should be before divisions to avoid losing
precision if scale is small:
https://www.linuxquestions.org/questions/ubuntu-63/shell-script-to-convert-celsius-to-fahrenheit-929261/
<https://www.linuxquestions.org/questions/ubuntu-63/shell-script-to-convert-celsius-to-fahrenheit-929261>
WARRANTY
Expand All @@ -88,8 +71,6 @@ WARRANTY
Tested with GNU Bash 5.0.
If useful, please consider sending feedback! =)
USAGE EXAMPLES
$ $SN 10
Expand All @@ -107,126 +88,132 @@ OPTIONS
-r Convert relative temperatures."


#don't convert
isSameUnitf()
{
local degsign
if [[ "${FROMT:-x}${HELPER+x}" = "$TOT" ]]
then [[ $TOT = [kK] ]] || degsign=º
printf '%s%s%s\n' "$*" "${HELPER-$'\t'$degsign}" "${HELPER-$TOT}"
return 2
fi
return 0
}

#calculator command
#bc fun
calcf()
{
bc <<-!
/* Round argument 'x' to 'd' digits */
define round(x, d) {
auto r, s
if(0 > x) {
return -round(-x, d)
}
r = x + 0.5*10^-d
s = scale
scale = d
r = r*10/10
scale = s
return r
};
/* Serge3leo - https://stackoverflow.com/questions/26861118/rounding-numbers-with-bc-in-bash
* MetroEast - https://askubuntu.com/questions/179898/how-to-round-decimals-using-bc-in-bash
*/
/* Truncate trailing zeroes */
define trunc(x){auto os;os=scale;for(scale=0;scale<=os;scale++)if(x==x/1){x/=1;scale=os;return x}}
/* http://phodd.net/gnu-bc/bcfaq.html
*/
scale=$SCALE+1; trunc( round($* , $SCALE) )
!
bc <<!
/* Round argument 'x' to 'd' digits */
define round(x, d) {
auto r, s
if(0 > x) {
return -round(-x, d)
}
r = x + 0.5*10^-d
s = scale
scale = d
r = r*10/10
scale = s
return r
};
/* Truncate trailing zeroes */
define trunc(x){auto os;os=scale;for(scale=0;scale<=os;scale++)if(x==x/1){x/=1;scale=os;return x}}
scale=${SCALE:-2}+1
trunc( round(${*} , ${SCALE:-2}) )
!
}
#Serge3leo - https://stackoverflow.com/questions/26861118
#MetroEast - https://askubuntu.com/questions/179898
#http://phodd.net/gnu-bc/bcfaq.html

#convert amongst absolute temps
absolutef()
{
local tot degsign res
typeset tot degsign res
typeset -u tot

#from fahrenheit
if [[ "$FROMT" = f ]] || [[ -z "$FROMT" && "$*" != "$TOGGLET" ]]
then [[ -z "$FROMT" ]] && TOGGLET="$*"
case "${FROMT}" in
#from fahrenheit
f|'') if [[ -n "${TOT}" && -z "${TOT%%f}" ]]
then tot=f res=$1
#to celsius
if [[ -z "${TOT/c}" ]]
elif [[ -z "${TOT%%c}" ]]
then tot=c res=$( calcf "( (${1}) - 32) * 5/9" )
#to kelvin
else tot=k res=$( calcf "( ( (${1}) - 32) * 5/9) + 273.15" )
fi
#from celsius
elif [[ "$FROMT" = c ]] || [[ -z "$FROMT" && "$*" = "$TOGGLET" ]]
then [[ -z "$FROMT" ]] && TOGGLET=
fi;;
#from celsius
c) if [[ -n "${TOT}" && -z "${TOT%%c}" ]]
then tot=c res=$1
#to fahrenheit
if [[ -z "${TOT/f}" ]]
elif [[ -z "${TOT%%f}" ]]
then tot=f res=$( calcf "( (${1}) * 9/5) + 32" )
#to kelvin
else tot=k res=$( calcf "(${1}) + 273.15/1" )
fi
#from kelvin
else if [[ -z "${TOT/c}" ]]
fi;;
#from kelvin
k|*) if [[ -n "${TOT}" && -z "${TOT%%k}" ]]
then tot=k res=$1
#to celsius
elif [[ -z "${TOT%%c}" ]]
then tot=c res=$( calcf "(${1}) - 273.15/1" )
#to fahrenheit
else tot=f res=$( calcf "( ( (${1}) - 273.15) * 9/5) + 32" )
fi
fi
fi;;
esac

[[ $tot = [kK] ]] || degsign=º
printf '%s%s%s\n' "$res" "${HELPER-$'\t'$degsign}" "${HELPER-$tot}"
((QUIET)) && printf '%s\n' "$res" ||
printf '%s %s%s\n' "$res" "$degsign" "$tot"
}

#convert amongst relative temps
relativef()
{
local degsign kzero kvar kdelta tzero tvar tdelta

#from farenheit or kelvin
if [[ "$FROMT$TOT" = [fk] ]] || [[ -z "$FROMT" && "$*" != "$TOGGLET" ]]
then [[ -z "$FROMT" ]] && FROMT=f TOGGLET="$*"
TOT=c
#from celsius
elif [[ "$FROMT$TOT" = c ]] || [[ -z "$FROMT" && "$*" = "$TOGGLET" ]]
then [[ -z "$FROMT" ]] && FROMT=c TOGGLET=
TOT=f
fi
typeset degsign kzero kvar kdelta tzero tvar tdelta

case "${FROMT}${TOT}" in
#from farenheit or null
f|'') [[ -z "$FROMT" ]] && FROMT=f
TOT=c;;
#from celsius or kelvin
c|k|*) [[ -z "$FROMT" ]] && FROMT=c
TOT=f;;
esac

#normalise temp unit for comparison in kelvin
kzero=$( HELPER= TOGGLET= TOT=k absolutef 0)
kvar=$( HELPER= TOGGLET= TOT=k absolutef "$1")
kzero=$(QUIET=1 TOT=k absolutef 0)
kvar=$(QUIET=1 TOT=k absolutef "$1")
kdelta=$(calcf "$kvar - ( $kzero )" )

#transform kelvin delta in target temp delta
tzero=$( HELPER= TOGGLET= FROMT=k absolutef 0 )
tvar=$( HELPER= TOGGLET= FROMT=k absolutef "$kdelta" )
tzero=$(QUIET=1 FROMT=k absolutef 0 )
tvar=$(QUIET=1 FROMT=k absolutef "$kdelta" )
tdelta=$(calcf "$tvar - ( $tzero )" )

[[ $TOT = [kK] ]] || degsign=º
printf '%s%s%s\n' "$tdelta" "${HELPER-$'\t'$degsign}" "${HELPER-$TOT}"
((QUIET)) && printf '%s\n' "$tdelta" ||
printf '%s %s%s\n' "$tdelta" "$degsign" "$TOT"
}
# Temperature unit conversions are nonlinear; for example, temper-
# ature conversions between Fahrenheit and Celsius scales cannot
# be done by simply multiplying by conversion factors. These equa-
# tions can only be used to convert from one specific temperature
# to another specific temperature; for example, you can show that
# the specific temperature of 0.0 Celsius equals 32 Fahrenheit, or
# that the specific temperature of 100 Celsius equals 212 Fahrenheit.
# However, a temperature difference of 100 degrees in the Celsius
# scale is the same as a temperature difference of 180 degrees in
# the Fahrenheit scale.


#prepare environment
export LC_NUMERIC=C


unset HELPER
#parse opts
while getopts 1234567890hHrRq c
while getopts 1234567890hHrRqv c
do case $c in
[0-9]) #scale
SCALE="${c%%[.,]*}"
SCALE="${SCALE}${c%%[.,]*}"
;;
[hH])
#help
echo "$HELP"
exit
;;
[qQ]) #quiet
HELPER=
[qv]) #quiet
QUIET=1
;;
[rR]) #relative temps
OPTR=relative
Expand All @@ -246,17 +233,14 @@ elif [[ "$*" != *[0-9]* && ! -t 0 ]]
then set -- $(</dev/stdin) "$@"
fi

typeset -l UNITS
UNITS="$*" UNITS="${UNITS//[^a-z]}" UNITS="${UNITS//celsius/c}"
typeset -l UNITS; UNITS="$*";
UNITS="${UNITS//[^a-z]}" UNITS="${UNITS//celsius/c}"
UNITS="${UNITS//farenheit/f}" UNITS="${UNITS//kelvin/k}"
if ILLEGAL='abdeg-jl-z' ;[[ "$UNITS" = *[$ILLEGAL]* ]]
then printf '%s: err: illegal unit -- %s\n' "$SN" "${UNITS//[^${ILLEGAL}]}" >&2
if [[ "$UNITS" = *[abdeg-jl-z]* ]]
then printf '%s: err: illegal unit -- %s\n' "$SN" "${UNITS//[^abdeg-jl-z]}" >&2
exit 2
fi

set -- "${@//[^0-9,.-]}" #remove units
set -- "${@/,/.}" #change comma to dot

#from-unit is always the first
(( ${#UNITS} )) && FROMT="${UNITS:0:1}"

Expand All @@ -265,26 +249,11 @@ set -- "${@/,/.}" #change comma to dot

[[ -n "$SCALE" ]] || SCALE="$SCALEDEF"

#toggle
TOGGLETTEMP="${TMPDIR:-/tmp}/$SN.$USER.togglet"
[[ -z "$FROMT" && -e "$TOGGLETTEMP" ]] && read TOGGLET <"$TOGGLETTEMP"
set -- "${@//[^0-9,.-]}" #remove units
set -- "${@/,/.}" #change comma to dot

#conversion type
isSameUnitf "$*" || exit
if [[ -n "$OPTR" ]]
then relativef "$@"
else absolutef "$@"
fi
code=$?

#set toggle temp file?
if [[ -n "$TOGGLET" ]]
then if ! echo "$TOGGLET" >"$TOGGLETTEMP"
then printf '%s: err: cannot create tmp file -- %s\n' "$SN" "$TOGGLETTEMP" >&2
exit 1
fi
elif [[ -e "$TOGGLETTEMP" ]]
then rm -- "$TOGGLETTEMP" || exit
fi

exit ${code:-0}

0 comments on commit c62496b

Please sign in to comment.