File: bu.sh
   1 #!/bin/sh
   2 
   3 # The MIT License (MIT)
   4 #
   5 # Copyright (c) 2026 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 [ "$1" = '--' ] && shift
  62 
  63 if [ -z "$1" ]; then
  64     awk '/^# +bu /, /^$/ { gsub(/^# ?/, ""); print }' "$0"
  65     exit 0
  66 fi
  67 
  68 # normalize the unit name right away, to avoid re-running the same apps
  69 unitname="$(echo "${2}" | tr "[:upper:]" "[:lower:]" | tr -d - | tr -d _)"
  70 
  71 case "${unitname}" in
  72     # handle that mixed-unit abomination using feet and inches
  73     ft|feet|foot)
  74         case "$(echo "${4}" | tr "[:upper:]" "[:lower:]")" in
  75             in|inch|inches)
  76                 y=$(echo "scale=4; 0.3048*${1} + 0.0254*${3}" | bc)
  77                 printf "%12.4f ft %12.4f in = %12.4f m\n" \
  78                     "${1}" "${3}" "${y}"
  79                 exit $?
  80             ;;
  81         esac
  82     ;;
  83 
  84     # handle hertz, megahertz, and gigahertz: even some old-fashioned names
  85     # are supported; the name `cycle` is handled as an angular unit instead
  86     hz|hertz)
  87         y=$(echo "scale=4; 1/${1}" | bc)
  88         printf "%12.4f hz = %12.4f s\n" "${1}" "${y}"
  89         exit $?
  90     ;;
  91     mhz|megahertz|megacycle|megacycles)
  92         y=$(echo "scale=12; 1/(1000000*${1})" | bc)
  93         printf "%12.4f hz = %12.12f s\n" "${1}" "${y}"
  94         exit $?
  95     ;;
  96     ghz|gigahertz|gigacycle|gigacycles)
  97         y=$(echo "scale=12; 1/(1000000000*${1})" | bc)
  98         printf "%12.4f hz = %12.12f s\n" "${1}" "${y}"
  99         exit $?
 100     ;;
 101 
 102     # handle that mixed-unit abomination using pounds and weight-ounces
 103     lb|lbs|pound|pounds)
 104         case "$(echo "${4}" | tr "[:upper:]" "[:lower:]")" in
 105             oz|ozs|ounce|ounces)
 106                 y=$(echo "scale=4; 0.45359237*${1} + 0.028349523*${3}" | bc)
 107                 printf "%12.4f lb %12.4f oz = %12.4f kg\n" \
 108                     "${1}" "${3}" "${y}"
 109                 exit $?
 110             ;;
 111         esac
 112     ;;
 113 
 114     # handle converting numbers from base 2 into base-10 ones
 115     bin|binary|base2|b2|0b|bit|bits|2)
 116         # check for valid binary values, quitting whole script on failure
 117         if echo "$1" | grep -E -q '.*[^01].*'; then
 118             printf "\e[31minvalid binary number %s\e[0m\n" "$1" > /dev/stderr
 119             exit 1
 120         fi
 121 
 122         decval=$(echo "ibase=2; ${1}" | bc)
 123         printf "%s bin = %s dec\n" "${1}" "${decval}"
 124         exit $?
 125     ;;
 126 
 127     # handle converting numbers from base 8 into base-10 ones
 128     oct|octal|base8|b8|0o|o|8)
 129         # check for valid binary values, quitting whole script on failure
 130         if echo "$1" | grep -E -q '.*[^0-7].*'; then
 131             printf "\e[31minvalid octal number %s\e[0m\n" "$1" > /dev/stderr
 132             exit 1
 133         fi
 134 
 135         decval=$(echo "ibase=8; ${1}" | bc)
 136         printf "%s oct = %s dec\n" "${1}" "${decval}"
 137         exit $?
 138     ;;
 139 
 140     # handle converting numbers from base 16 into base-10 ones
 141     hex|hexa|hexadec|hexadecimal|base16|b16|0x|x|16)
 142         hexval=$(echo "${1}" | sed 's-^0x--' | tr '[:lower:]' '[:upper:]')
 143         if echo "${hexval}" | grep -E -q '.*[^0-9a-fA-F].*'; then
 144             printf "\e[31minvalid octal number %s\e[0m\n" "$1" > /dev/stderr
 145             exit 1
 146         fi
 147 
 148         decval=$(echo "ibase=16; ${hexval}" | bc)
 149         printf "%s hex = %s dec\n" "${hexval}" "${decval}"
 150         exit $?
 151     ;;
 152 esac
 153 
 154 # warn users who might wrongly think multiple pairs of args are allowed
 155 # in general: only ft/in and lb/oz are allowed
 156 if [ $# -gt 2 ]; then
 157     printf "\e[31m only 1 or 2 args allowed, unless using ft/in or " >&2
 158     printf "lb/oz mixed-units value-pairs\e[0m\n" >&2
 159     exit 1
 160 fi
 161 
 162 # handle all other unit-conversions
 163 awk -v x="${1:-0}" -v unit="${unitname}" '
 164     # normalize both quantities and units, and handle special cases for units
 165     BEGIN {
 166         # ignore underscores in numbers, thus `allowing` them for convenience
 167         gsub(/[_]/, "", x)
 168 
 169         if (x + 0 == 0 && x !~ /0+/) {
 170             fs = "\x1b[31mvalue `%s` not a valid number\n\x1b[0m"
 171             printf fs, unit > "/dev/stderr"
 172             good_unit = 1
 173             exit 1
 174         }
 175 
 176         # normalize unit names for later lookup
 177         unit = tolower(unit)
 178         gsub(/[ _.-]/, "", unit)
 179 
 180         # handle fahrenheit temperatures
 181         if (unit == "f" || unit == "fahrenheit") {
 182             printf "%12.4f °F = %12.4f °C\n", x, (x - 32) * (5.0/9.0)
 183             good_unit = 1
 184             exit 0
 185         }
 186 
 187         # handle kelvin temperatures
 188         if (unit == "k" || unit == "kelvin") {
 189             printf "%12.4f °K = %12.4f °C\n", x, x - 273.15
 190             good_unit = 1
 191             exit 0
 192         }
 193 
 194         # handle the mm:ss time format
 195         if (match(x, /^[0-9]+:[0-9]{1,2}(\.[0-9]*)?$/)) {
 196             split(x, hms, ":")
 197             printf "%s = %12.4f s\n", x, 60*hms[1] + hms[2]
 198             good_unit = 1
 199             exit 0
 200         }
 201 
 202         # handle the hh:mm:ss time format
 203         if (match(x, /^[0-9]+:[0-9]{1,2}:[0-9]{1,2}(\.[0-9]*)?$/)) {
 204             split(x, hms, ":")
 205             printf "%s = %12.4f s\n", x, 3600*hms[1] + 60*hms[2] + hms[3]
 206             good_unit = 1
 207             exit 0
 208         }
 209 
 210         # handle seconds, turning the number into the hh:mm:ss time format
 211         if (unit == "s") {
 212             h = (x - x % 3600) / 3600
 213             m = (x % 3600) / 60
 214             s = x % 60
 215             printf "%s s = %02d:%02d:%05.2f\n", x, h, m, s
 216             good_unit = 1
 217             exit 0
 218         }
 219     }
 220 
 221     # match the unit given to its built-in entry, if present
 222     $1 == unit {
 223         printf "%12.4f %s = %12.4f %s\n", x, $2, $3*x, $4
 224         good_unit = 1
 225         exit 0
 226     }
 227 
 228     # handle case where no match was found
 229     END {
 230         if (!good_unit) {
 231             printf "unit name/alias `%s` not supported\n", unit > "/dev/stderr"
 232             exit 1
 233         }
 234     }
 235 ' << 'EOF'
 236 ac              ac    4046.8564224     m²
 237 acre            ac    4046.8564224     m²
 238 acres           ac    4046.8564224     m²
 239 angle           deg   0.0174532925199  rad
 240 andeg           deg   0.0174532925199  rad
 241 angdeg          deg   0.0174532925199  rad
 242 angular         deg   0.0174532925199  rad
 243 angulardeg      deg   0.0174532925199  rad
 244 angulardegree   deg   0.0174532925199  rad
 245 angulardegrees  deg   0.0174532925199  rad
 246 angledeg        deg   0.0174532925199  rad
 247 angledegree     deg   0.0174532925199  rad
 248 angledegrees    deg   0.0174532925199  rad
 249 barrel          bbl   158.9873         L
 250 barrels         bbl   158.9873         L
 251 bbl             bbl   158.9873         L
 252 cfeet           ft³   0.028316846592   m³
 253 cfoot           ft³   0.028316846592   m³
 254 cft             ft³   0.028316846592   m³
 255 cufeet          ft³   0.028316846592   m³
 256 cufoot          ft³   0.028316846592   m³
 257 cuft            ft³   0.028316846592   m³
 258 cup             cup   0.23658824       L
 259 cups            cup   0.23658824       L
 260 cyc             tr    6.2831853071796  rad
 261 cycle           tr    6.2831853071796  rad
 262 cycles          tr    6.2831853071796  rad
 263 d               day   86400            s
 264 day             day   86400            s
 265 days            day   86400            s
 266 deg             deg   0.0174532925199  rad
 267 degree          deg   0.0174532925199  rad
 268 degrees         deg   0.0174532925199  rad
 269 dwt             dwt   1.55517384       g
 270 dwts            dwt   1.55517384       g
 271 in              in    2.54             cm
 272 inch            in    2.54             cm
 273 inches          in    2.54             cm
 274 ft              ft    0.3048           m
 275 ft2             ft²   0.09290304       m²
 276 ft3             ft³   0.028316846592   m³
 277 ft²             ft²   0.09290304       m²
 278 ft³             ft³   0.028316846592   m³
 279 feet            ft    0.3048           m
 280 feet2           ft²   0.09290304       m²
 281 feet3           ft³   0.028316846592   m³
 282 feet²           ft²   0.09290304       m²
 283 feet³           ft³   0.028316846592   m³
 284 floz            floz  29.5735295625    mL
 285 foot            ft    0.3048           m
 286 foot2           ft²   0.09290304       m²
 287 foot3           ft³   0.028316846592   m³
 288 foot²           ft²   0.09290304       m²
 289 foot³           ft³   0.028316846592   m³
 290 ga              gal   3.785411784      L
 291 gal             gal   3.785411784      L
 292 gallon          gal   3.785411784      L
 293 gallons         gal   3.785411784      L
 294 gb              gb    1073741824       bytes
 295 gib             gb    1073741824       bytes
 296 gon             grad  0.0157079632679  rad
 297 gons            grad  0.0157079632679  rad
 298 grad            grad  0.0157079632679  rad
 299 gradian         grad  0.0157079632679  rad
 300 gradians        grad  0.0157079632679  rad
 301 h               hr    3600             s
 302 hour            hr    3600             s
 303 hours           hr    3600             s
 304 hr              hr    3600             s
 305 hrs             hr    3600             s
 306 kb              kb    1024             bytes
 307 kib             kb    1024             bytes
 308 lb              lb    0.45359237       kg
 309 lbs             lb    0.45359237       kg
 310 lbst            lbt   373.2417216      g
 311 lbt             lbt   373.2417216      g
 312 mb              mb    1048576          bytes
 313 mi              mi    1.609344         km
 314 mib             mb    1048576          bytes
 315 mih             mph   1.609344         kph
 316 mi2             mi²   2.5899881103360  km²
 317 mi²             mi²   2.5899881103360  km²
 318 mil             mrad  0.001            rad
 319 millirad        mrad  0.001            rad
 320 milliradian     mrad  0.001            rad
 321 milliradians    mrad  0.001            rad
 322 mile            mi    1.609344         km
 323 mile²           mi²   2.5899881103360  km²
 324 miles           mi    1.609344         km
 325 miles²          mi²   2.5899881103360  km²
 326 min             min   60               s
 327 minute          min   60               s
 328 minutes         min   60               s
 329 mpg             mpg   0.425143707      kpl
 330 mph             mph   1.609344         kph
 331 mrad            mrad  0.001            rad
 332 nmi             nmi   1.852            km
 333 nmile           nmi   1.852            km
 334 nmiles          nmi   1.852            km
 335 oilbarrel       bbl   158.9873         L
 336 oilbarrels      bbl   158.9873         L
 337 oz              oz    28.349523125     g
 338 ozt             ozt   31.1034768       g
 339 ounce           oz    28.349523125     g
 340 ounces          oz    28.349523125     g
 341 pennyweight     dwt   1.55517384       g
 342 pint            pt    473.176473       mL
 343 pla             tr    6.2831853071796  rad
 344 pound           lb    0.45359237       kg
 345 pounds          lb    0.45359237       kg
 346 psi             psi   6894.757293168   Pa
 347 pt              pt    473.176473       mL
 348 rad             rad   57.295779513082  deg
 349 radian          rad   57.295779513082  deg
 350 radians         rad   57.295779513082  deg
 351 rev             tr    6.2831853071796  rad
 352 revolution      tr    6.2831853071796  rad
 353 revolutions     tr    6.2831853071796  rad
 354 sfeet           ft²   0.09290304       m²
 355 sfoot           ft²   0.09290304       m²
 356 sft             ft²   0.09290304       m²
 357 smi             mi²   2.5899881103360  km²
 358 smile           mi²   2.5899881103360  km²
 359 smiles          mi²   2.5899881103360  km²
 360 syard           yd²   0.83612736       m²
 361 syards          yd²   0.83612736       m²
 362 syd             yd²   0.83612736       m²
 363 syds            yd²   0.83612736       m²
 364 sqfeet          ft²   0.09290304       m²
 365 sqfoot          ft²   0.09290304       m²
 366 sqft            ft²   0.09290304       m²
 367 sqmi            mi²   2.5899881103360  km²
 368 sqmile          mi²   2.5899881103360  km²
 369 sqmiles         mi²   2.5899881103360  km²
 370 sqyard          yd²   0.83612736       m²
 371 sqyards         yd²   0.83612736       m²
 372 sqyd            yd²   0.83612736       m²
 373 sqyds           yd²   0.83612736       m²
 374 tb              tb    1099511627776    bytes
 375 tib             tb    1099511627776    bytes
 376 ton             ton   907.18474        kg
 377 tr              tr    6.2831853071796  rad
 378 troyounce       ozt   31.1034768       g
 379 troyounces      ozt   31.1034768       g
 380 troypound       lbt   373.2417216      g
 381 troypounds      lbt   373.2417216      g
 382 turn            tr    6.2831853071796  rad
 383 turns           tr    6.2831853071796  rad
 384 ukpint          ukpt  568.26125        mL
 385 ukpt            ukpt  568.26125        mL
 386 uspint          pt    473.176473       mL
 387 uspt            pt    473.176473       mL
 388 w               wk    604800           s
 389 week            wk    604800           s
 390 weeks           wk    604800           s
 391 wk              wk    604800           s
 392 wks             wk    604800           s
 393 yard            yd    0.9144           m
 394 yard2           yd²   0.83612736       m²
 395 yards           yd    0.9144           m
 396 yards2          yd²   0.83612736       m²
 397 yd              yd    0.9144           m
 398 yds             yd    0.9144           m
 399 yd²             yd²   0.83612736       m²
 400 yds²            yd²   0.83612736       m²
 401 EOF