File: bu.sh 1 #!/bin/sh 2 3 # The MIT License (MIT) 4 # 5 # Copyright © 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 [ "$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 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