File: bu.sh
   1 #!/bin/sh
   2 
   3 # The MIT License (MIT)
   4 #
   5 # Copyright © 2020-2025 pacman64
   6 #
   7 # Permission is hereby granted, free of charge, to any person obtaining a copy
   8 # of this software and associated documentation files (the “Software”), to deal
   9 # in the Software without restriction, including without limitation the rights
  10 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11 # copies of the Software, and to permit persons to whom the Software is
  12 # furnished to do so, subject to the following conditions:
  13 #
  14 # The above copyright notice and this permission notice shall be included in
  15 # all copies or substantial portions of the Software.
  16 #
  17 # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  23 # SOFTWARE.
  24 
  25 
  26 # bu [time]
  27 # bu [quantity] [unit]
  28 # bu [quantity] ft [quantity] in
  29 # bu [quantity] lb [quantity] oz
  30 #
  31 # Better Units
  32 #
  33 #
  34 # Turn common measurement units used in the US into their international
  35 # counterparts. Even the mixed-unit cases ft-in and lb-oz are accepted,
  36 # when given 2 number-unit pairs.
  37 #
  38 # Besides those, time units in common use, such as minutes, hours, days,
  39 # and weeks have been added for convenience: these are all turned into
  40 # seconds.
  41 #
  42 # Even time-formats HH:MM:SS and MM:SS are supported: the seconds part
  43 # can optionally have decimal digits, and no unit is needed, as those
  44 # formats clearly identify their time-related units by themselves.
  45 #
  46 #
  47 # You may also want to use the shortcut below, instead of using this
  48 # script directly.
  49 #
  50 # # Change Units converts common units into more convenient equivalents
  51 # cu() { bu "$@" | awk '{ print $(NF-1), $NF }'; }
  52 
  53 
  54 case "$1" in
  55     -h|--h|-help|--help)
  56         awk '/^# +bu /, /^$/ { gsub(/^# ?/, ""); print }' "$0"
  57         exit 0
  58     ;;
  59 esac
  60 
  61 if [ -z "$1" ]; then
  62     awk '/^# +bu /, /^$/ { gsub(/^# ?/, ""); print }' "$0"
  63     exit 0
  64 fi
  65 
  66 # normalize the unit name right away, to avoid re-running the same apps
  67 unitname="$(echo "${2}" | tr "[:upper:]" "[:lower:]" | tr -d - | tr -d _)"
  68 
  69 case "${unitname}" in
  70     # handle that mixed-unit abomination using feet and inches
  71     ft|feet|foot)
  72         case "$(echo "${4}" | tr "[:upper:]" "[:lower:]")" in
  73             in|inch|inches)
  74                 y=$(echo "scale=4; 0.3048*${1} + 0.0254*${3}" | bc)
  75                 printf "%12.4f ft %12.4f in = %12.4f m\n" \
  76                     "${1}" "${3}" "${y}"
  77                 exit $?
  78             ;;
  79         esac
  80     ;;
  81 
  82     # handle hertz, megahertz, and gigahertz: even some old-fashioned names
  83     # are supported; the name `cycle` is handled as an angular unit instead
  84     hz|hertz)
  85         y=$(echo "scale=4; 1/${1}" | bc)
  86         printf "%12.4f hz = %12.4f s\n" "${1}" "${y}"
  87         exit $?
  88     ;;
  89     mhz|megahertz|megacycle|megacycles)
  90         y=$(echo "scale=12; 1/(1000000*${1})" | bc)
  91         printf "%12.4f hz = %12.12f s\n" "${1}" "${y}"
  92         exit $?
  93     ;;
  94     ghz|gigahertz|gigacycle|gigacycles)
  95         y=$(echo "scale=12; 1/(1000000000*${1})" | bc)
  96         printf "%12.4f hz = %12.12f s\n" "${1}" "${y}"
  97         exit $?
  98     ;;
  99 
 100     # handle that mixed-unit abomination using pounds and weight-ounces
 101     lb|lbs|pound|pounds)
 102         case "$(echo "${4}" | tr "[:upper:]" "[:lower:]")" in
 103             oz|ozs|ounce|ounces)
 104                 y=$(echo "scale=4; 0.45359237*${1} + 0.028349523*${3}" | bc)
 105                 printf "%12.4f lb %12.4f oz = %12.4f kg\n" \
 106                     "${1}" "${3}" "${y}"
 107                 exit $?
 108             ;;
 109         esac
 110     ;;
 111 
 112     # handle converting numbers from base 2 into base-10 ones
 113     bin|binary|base2|b2|0b|bit|bits|2)
 114         # check for valid binary values, quitting whole script on failure
 115         if echo "$1" | grep -E -q '.*[^01].*'; then
 116             printf "\e[31minvalid binary number %s\e[0m\n" "$1" > /dev/stderr
 117             exit 1
 118         fi
 119 
 120         decval=$(echo "ibase=2; ${1}" | bc)
 121         printf "%s bin = %s dec\n" "${1}" "${decval}"
 122         exit $?
 123     ;;
 124 
 125     # handle converting numbers from base 8 into base-10 ones
 126     oct|octal|base8|b8|0o|o|8)
 127         # check for valid binary values, quitting whole script on failure
 128         if echo "$1" | grep -E -q '.*[^0-7].*'; then
 129             printf "\e[31minvalid octal number %s\e[0m\n" "$1" > /dev/stderr
 130             exit 1
 131         fi
 132 
 133         decval=$(echo "ibase=8; ${1}" | bc)
 134         printf "%s oct = %s dec\n" "${1}" "${decval}"
 135         exit $?
 136     ;;
 137 
 138     # handle converting numbers from base 16 into base-10 ones
 139     hex|hexa|hexadec|hexadecimal|base16|b16|0x|x|16)
 140         hexval=$(echo "${1}" | sed 's-^0x--' | tr '[:lower:]' '[:upper:]')
 141         if echo "${hexval}" | grep -E -q '.*[^0-9a-fA-F].*'; then
 142             printf "\e[31minvalid octal number %s\e[0m\n" "$1" > /dev/stderr
 143             exit 1
 144         fi
 145 
 146         decval=$(echo "ibase=16; ${hexval}" | bc)
 147         printf "%s hex = %s dec\n" "${hexval}" "${decval}"
 148         exit $?
 149     ;;
 150 esac
 151 
 152 # warn users who might wrongly think multiple pairs of args are allowed
 153 # in general: only ft/in and lb/oz are allowed
 154 if [ $# -gt 2 ]; then
 155     printf "\e[31m only 1 or 2 args allowed, unless using ft/in or " >&2
 156     printf "lb/oz mixed-units value-pairs\e[0m\n" >&2
 157     exit 1
 158 fi
 159 
 160 # handle all other unit-conversions
 161 awk -v x="${1:-0}" -v unit="${unitname}" '
 162 # normalize both quantities and units, and handle special cases for units
 163 BEGIN {
 164     # ignore underscores in numbers, thus `allowing` them for convenience
 165     gsub(/[_]/, "", x)
 166 
 167     if (x + 0 == 0 && x !~ /0+/) {
 168         fs = "\x1b[31mvalue `%s` not a valid number\n\x1b[0m"
 169         printf fs, unit > "/dev/stderr"
 170         good_unit = 1
 171         exit 1
 172     }
 173 
 174     # normalize unit names for later lookup
 175     unit = tolower(unit)
 176     gsub(/[ _.-]/, "", unit)
 177 
 178     # handle fahrenheit temperatures
 179     if (unit == "f" || unit == "fahrenheit") {
 180         printf "%12.4f °F = %12.4f °C\n", x, (x - 32) * (5.0/9.0)
 181         good_unit = 1
 182         exit 0
 183     }
 184 
 185     # handle kelvin temperatures
 186     if (unit == "k" || unit == "kelvin") {
 187         printf "%12.4f °K = %12.4f °C\n", x, x - 273.15
 188         good_unit = 1
 189         exit 0
 190     }
 191 
 192     # handle the mm:ss time format
 193     if (match(x, /^[0-9]+:[0-9]{1,2}(\.[0-9]*)?$/)) {
 194         split(x, hms, ":")
 195         printf "%s = %12.4f s\n", x, 60*hms[1] + hms[2]
 196         good_unit = 1
 197         exit 0
 198     }
 199 
 200     # handle the hh:mm:ss time format
 201     if (match(x, /^[0-9]+:[0-9]{1,2}:[0-9]{1,2}(\.[0-9]*)?$/)) {
 202         split(x, hms, ":")
 203         printf "%s = %12.4f s\n", x, 3600*hms[1] + 60*hms[2] + hms[3]
 204         good_unit = 1
 205         exit 0
 206     }
 207 
 208     # handle seconds, turning the number into the hh:mm:ss time format
 209     if (unit == "s") {
 210         h = (x - x % 3600) / 3600
 211         m = (x % 3600) / 60
 212         s = x % 60
 213         printf "%s s = %02d:%02d:%05.2f\n", x, h, m, s
 214         good_unit = 1
 215         exit 0
 216     }
 217 }
 218 
 219 # match the unit given to its built-in entry, if present
 220 $1 == unit {
 221     printf "%12.4f %s = %12.4f %s\n", x, $2, $3*x, $4
 222     good_unit = 1
 223     exit 0
 224 }
 225 
 226 # tell user when no match was found, and fail the whole surrounding tool if so
 227 END {
 228     if (!good_unit) {
 229         fs = "\x1b[31munit name/alias `%s` not supported\n\x1b[0m"
 230         printf fs, unit > "/dev/stderr"
 231         exit 1
 232     }
 233 }
 234 ' << 'EOF'
 235 ac              ac    4046.8564224     m²
 236 acre            ac    4046.8564224     m²
 237 acres           ac    4046.8564224     m²
 238 angle           deg   0.0174532925199  rad
 239 andeg           deg   0.0174532925199  rad
 240 angdeg          deg   0.0174532925199  rad
 241 angular         deg   0.0174532925199  rad
 242 angulardeg      deg   0.0174532925199  rad
 243 angulardegree   deg   0.0174532925199  rad
 244 angulardegrees  deg   0.0174532925199  rad
 245 angledeg        deg   0.0174532925199  rad
 246 angledegree     deg   0.0174532925199  rad
 247 angledegrees    deg   0.0174532925199  rad
 248 barrel          bbl   158.9873         L
 249 barrels         bbl   158.9873         L
 250 bbl             bbl   158.9873         L
 251 cfeet           ft³   0.028316846592   m³
 252 cfoot           ft³   0.028316846592   m³
 253 cft             ft³   0.028316846592   m³
 254 cufeet          ft³   0.028316846592   m³
 255 cufoot          ft³   0.028316846592   m³
 256 cuft            ft³   0.028316846592   m³
 257 cup             cup   0.23658824       L
 258 cups            cup   0.23658824       L
 259 cyc             tr    6.2831853071796  rad
 260 cycle           tr    6.2831853071796  rad
 261 cycles          tr    6.2831853071796  rad
 262 d               day   86400            s
 263 day             day   86400            s
 264 days            day   86400            s
 265 deg             deg   0.0174532925199  rad
 266 degree          deg   0.0174532925199  rad
 267 degrees         deg   0.0174532925199  rad
 268 in              in    2.54             cm
 269 inch            in    2.54             cm
 270 inches          in    2.54             cm
 271 ft              ft    0.3048           m
 272 ft2             ft²   0.09290304       m²
 273 ft3             ft³   0.028316846592   m³
 274 ft²             ft²   0.09290304       m²
 275 ft³             ft³   0.028316846592   m³
 276 feet            ft    0.3048           m
 277 feet2           ft²   0.09290304       m²
 278 feet3           ft³   0.028316846592   m³
 279 feet²           ft²   0.09290304       m²
 280 feet³           ft³   0.028316846592   m³
 281 floz            floz  29.5735295625    mL
 282 foot            ft    0.3048           m
 283 foot2           ft²   0.09290304       m²
 284 foot3           ft³   0.028316846592   m³
 285 foot²           ft²   0.09290304       m²
 286 foot³           ft³   0.028316846592   m³
 287 ga              gal   3.785411784      L
 288 gal             gal   3.785411784      L
 289 gallon          gal   3.785411784      L
 290 gallons         gal   3.785411784      L
 291 gb              gb    1073741824       bytes
 292 gib             gb    1073741824       bytes
 293 gon             grad  0.0157079632679  rad
 294 gons            grad  0.0157079632679  rad
 295 grad            grad  0.0157079632679  rad
 296 gradian         grad  0.0157079632679  rad
 297 gradians        grad  0.0157079632679  rad
 298 h               hr    3600             s
 299 hour            hr    3600             s
 300 hours           hr    3600             s
 301 hr              hr    3600             s
 302 hrs             hr    3600             s
 303 kb              kb    1024             bytes
 304 kib             kb    1024             bytes
 305 lb              lb    0.45359237       kg
 306 lbs             lb    0.45359237       kg
 307 mb              mb    1048576          bytes
 308 mi              mi    1.609344         km
 309 mib             mb    1048576          bytes
 310 mih             mph   1.609344         kph
 311 mi2             mi²   2.5899881103360  km²
 312 mi²             mi²   2.5899881103360  km²
 313 mil             mrad  0.001            rad
 314 millirad        mrad  0.001            rad
 315 milliradian     mrad  0.001            rad
 316 milliradians    mrad  0.001            rad
 317 mile            mi    1.609344         km
 318 mile²           mi²   2.5899881103360  km²
 319 miles           mi    1.609344         km
 320 miles²          mi²   2.5899881103360  km²
 321 min             min   60               s
 322 minute          min   60               s
 323 minutes         min   60               s
 324 mpg             mpg   0.425143707      kpl
 325 mph             mph   1.609344         kph
 326 mrad            mrad  0.001            rad
 327 nmi             nmi   1.852            km
 328 nmile           nmi   1.852            km
 329 nmiles          nmi   1.852            km
 330 oilbarrel       bbl   158.9873         L
 331 oilbarrels      bbl   158.9873         L
 332 oz              oz    28.349523125     g
 333 ounce           oz    28.349523125     g
 334 ounces          oz    28.349523125     g
 335 pint            pt    473.176473       mL
 336 pla             tr    6.2831853071796  rad
 337 pound           lb    0.45359237       kg
 338 pounds          lb    0.45359237       kg
 339 psi             psi   6894.757293168   Pa
 340 pt              pt    473.176473       mL
 341 rad             rad   57.295779513082  deg
 342 radian          rad   57.295779513082  deg
 343 radians         rad   57.295779513082  deg
 344 rev             tr    6.2831853071796  rad
 345 revolution      tr    6.2831853071796  rad
 346 revolutions     tr    6.2831853071796  rad
 347 sfeet           ft²   0.09290304       m²
 348 sfoot           ft²   0.09290304       m²
 349 sft             ft²   0.09290304       m²
 350 smi             mi²   2.5899881103360  km²
 351 smile           mi²   2.5899881103360  km²
 352 smiles          mi²   2.5899881103360  km²
 353 syard           yd²   0.83612736       m²
 354 syards          yd²   0.83612736       m²
 355 syd             yd²   0.83612736       m²
 356 syds            yd²   0.83612736       m²
 357 sqfeet          ft²   0.09290304       m²
 358 sqfoot          ft²   0.09290304       m²
 359 sqft            ft²   0.09290304       m²
 360 sqmi            mi²   2.5899881103360  km²
 361 sqmile          mi²   2.5899881103360  km²
 362 sqmiles         mi²   2.5899881103360  km²
 363 sqyard          yd²   0.83612736       m²
 364 sqyards         yd²   0.83612736       m²
 365 sqyd            yd²   0.83612736       m²
 366 sqyds           yd²   0.83612736       m²
 367 tb              tb    1099511627776    bytes
 368 tib             tb    1099511627776    bytes
 369 ton             ton   907.18474        kg
 370 tr              tr    6.2831853071796  rad
 371 turn            tr    6.2831853071796  rad
 372 turns           tr    6.2831853071796  rad
 373 ukpint          ukpt  568.26125        mL
 374 ukpt            ukpt  568.26125        mL
 375 uspint          pt    473.176473       mL
 376 uspt            pt    473.176473       mL
 377 w               wk    604800           s
 378 week            wk    604800           s
 379 weeks           wk    604800           s
 380 wk              wk    604800           s
 381 wks             wk    604800           s
 382 yard            yd    0.9144           m
 383 yard2           yd²   0.83612736       m²
 384 yards           yd    0.9144           m
 385 yards2          yd²   0.83612736       m²
 386 yd              yd    0.9144           m
 387 yds             yd    0.9144           m
 388 yd²             yd²   0.83612736       m²
 389 yds²            yd²   0.83612736       m²
 390 EOF