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