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