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 [ $# -le 1 ]; then 64 awk '/^# +bu /, /^$/ { gsub(/^# ?/, ""); print }' "$0" 65 exit 0 66 fi 67 68 if echo "$2" | tr -d _ | grep -E -q '^[+-]?([0-9]*\.[0-9]+|[0-9]+)$'; then 69 u="$1" 70 x="$2" 71 else 72 x="$1" 73 u="$2" 74 fi 75 76 # normalize the unit name right away, to avoid re-running the same apps 77 unitname="$(echo "$u" | tr "[:upper:]" "[:lower:]" | tr -d - | tr -d _)" 78 79 case "${unitname}" in 80 # handle that mixed-unit abomination using feet and inches 81 ft|feet|foot) 82 case "$(echo "$4" | tr "[:upper:]" "[:lower:]")" in 83 in|inch|inches) 84 y=$(echo "scale=4; 0.3048*$x + 0.0254*$3" | bc) 85 printf "%12.4f ft %12.4f in = %12.4f m\n" \ 86 "$x" "$3" "$y" 87 exit $? 88 ;; 89 esac 90 ;; 91 92 # handle hertz, megahertz, and gigahertz: even some old-fashioned names 93 # are supported; the name `cycle` is handled as an angular unit instead 94 hz|hertz) 95 y=$(echo "scale=4; 1/$x" | bc) 96 printf "%12.4f hz = %12.4f s\n" "$x" "$y" 97 exit $? 98 ;; 99 mhz|megahertz|megacycle|megacycles) 100 y=$(echo "scale=12; 1/(1000000*$x)" | bc) 101 printf "%12.4f hz = %12.12f s\n" "$x" "$y" 102 exit $? 103 ;; 104 ghz|gigahertz|gigacycle|gigacycles) 105 y=$(echo "scale=12; 1/(1000000000*$x)" | bc) 106 printf "%12.4f hz = %12.12f s\n" "$x" "$y" 107 exit $? 108 ;; 109 110 # handle that mixed-unit abomination using pounds and weight-ounces 111 lb|lbs|pound|pounds) 112 case "$(echo "$4" | tr "[:upper:]" "[:lower:]")" in 113 oz|ozs|ounce|ounces) 114 y=$(echo "scale=4; 0.45359237*$x + 0.028349523*$3" | bc) 115 printf "%12.4f lb %12.4f oz = %12.4f kg\n" \ 116 "$x" "$3" "$y" 117 exit $? 118 ;; 119 esac 120 ;; 121 122 # handle converting numbers from base 2 into base-10 ones 123 bin|binary|base2|b2|0b|bit|bits|2) 124 # check for valid binary values, quitting whole script on failure 125 if echo "$x" | grep -E -q '.*[^01].*'; then 126 printf "invalid binary number %s\n" "$x" > /dev/stderr 127 exit 1 128 fi 129 130 decval=$(echo "ibase=2; $x" | bc) 131 printf "%s bin = %s dec\n" "$x" "${decval}" 132 exit $? 133 ;; 134 135 # handle converting numbers from base 8 into base-10 ones 136 oct|octal|base8|b8|0o|o|8) 137 # check for valid binary values, quitting whole script on failure 138 if echo "$x" | grep -E -q '.*[^0-7].*'; then 139 printf "invalid octal number %s\n" "$x" > /dev/stderr 140 exit 1 141 fi 142 143 decval=$(echo "ibase=8; $x" | bc) 144 printf "%s oct = %s dec\n" "$x" "${decval}" 145 exit $? 146 ;; 147 148 # handle converting numbers from base 16 into base-10 ones 149 hex|hexa|hexadec|hexadecimal|base16|b16|0x|x|16) 150 hexval=$(echo "$x" | sed 's-^0x--' | tr '[:lower:]' '[:upper:]') 151 if echo "${hexval}" | grep -E -q '.*[^0-9a-fA-F].*'; then 152 printf "invalid octal number %s\n" "$x" > /dev/stderr 153 exit 1 154 fi 155 156 decval=$(echo "ibase=16; ${hexval}" | bc) 157 printf "%s hex = %s dec\n" "${hexval}" "${decval}" 158 exit $? 159 ;; 160 esac 161 162 # handle all other unit-conversions 163 awk -v x="$x" -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 = "value `%s` not a valid number\n" 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 fmt = "unit name/alias `%s` not supported\n" 232 printf fmt, unit > "/dev/stderr" 233 exit 1 234 } 235 } 236 ' << 'EOF' 237 ac ac 4046.8564224 m² 238 acre ac 4046.8564224 m² 239 acres ac 4046.8564224 m² 240 angle deg 0.0174532925199 rad 241 andeg deg 0.0174532925199 rad 242 angdeg deg 0.0174532925199 rad 243 angular deg 0.0174532925199 rad 244 angulardeg deg 0.0174532925199 rad 245 angulardegree deg 0.0174532925199 rad 246 angulardegrees deg 0.0174532925199 rad 247 angledeg deg 0.0174532925199 rad 248 angledegree deg 0.0174532925199 rad 249 angledegrees deg 0.0174532925199 rad 250 barrel bbl 158.9873 L 251 barrels bbl 158.9873 L 252 bbl bbl 158.9873 L 253 cfeet ft³ 0.028316846592 m³ 254 cfoot ft³ 0.028316846592 m³ 255 cft ft³ 0.028316846592 m³ 256 cufeet ft³ 0.028316846592 m³ 257 cufoot ft³ 0.028316846592 m³ 258 cuft ft³ 0.028316846592 m³ 259 cup cup 0.23658824 L 260 cups cup 0.23658824 L 261 cyc tr 6.2831853071796 rad 262 cycle tr 6.2831853071796 rad 263 cycles tr 6.2831853071796 rad 264 d day 86400 s 265 day day 86400 s 266 days day 86400 s 267 deg deg 0.0174532925199 rad 268 degree deg 0.0174532925199 rad 269 degrees deg 0.0174532925199 rad 270 dwt dwt 1.55517384 g 271 dwts dwt 1.55517384 g 272 in in 2.54 cm 273 inch in 2.54 cm 274 inches in 2.54 cm 275 ft ft 0.3048 m 276 ft2 ft² 0.09290304 m² 277 ft3 ft³ 0.028316846592 m³ 278 ft² ft² 0.09290304 m² 279 ft³ ft³ 0.028316846592 m³ 280 feet ft 0.3048 m 281 feet2 ft² 0.09290304 m² 282 feet3 ft³ 0.028316846592 m³ 283 feet² ft² 0.09290304 m² 284 feet³ ft³ 0.028316846592 m³ 285 floz floz 29.5735295625 mL 286 foot ft 0.3048 m 287 foot2 ft² 0.09290304 m² 288 foot3 ft³ 0.028316846592 m³ 289 foot² ft² 0.09290304 m² 290 foot³ ft³ 0.028316846592 m³ 291 ga gal 3.785411784 L 292 gal gal 3.785411784 L 293 gallon gal 3.785411784 L 294 gallons gal 3.785411784 L 295 gb gb 1073741824 bytes 296 gib gb 1073741824 bytes 297 gon grad 0.0157079632679 rad 298 gons grad 0.0157079632679 rad 299 grad grad 0.0157079632679 rad 300 gradian grad 0.0157079632679 rad 301 gradians grad 0.0157079632679 rad 302 h hr 3600 s 303 hour hr 3600 s 304 hours hr 3600 s 305 hr hr 3600 s 306 hrs hr 3600 s 307 kb kb 1024 bytes 308 kib kb 1024 bytes 309 lb lb 0.45359237 kg 310 lbs lb 0.45359237 kg 311 lbst lbt 373.2417216 g 312 lbt lbt 373.2417216 g 313 mb mb 1048576 bytes 314 mi mi 1.609344 km 315 mib mb 1048576 bytes 316 mih mph 1.609344 kph 317 mi2 mi² 2.5899881103360 km² 318 mi² mi² 2.5899881103360 km² 319 mil mrad 0.001 rad 320 millirad mrad 0.001 rad 321 milliradian mrad 0.001 rad 322 milliradians mrad 0.001 rad 323 mile mi 1.609344 km 324 mile² mi² 2.5899881103360 km² 325 miles mi 1.609344 km 326 miles² mi² 2.5899881103360 km² 327 min min 60 s 328 minute min 60 s 329 minutes min 60 s 330 mpg mpg 0.425143707 kpl 331 mph mph 1.609344 kph 332 mrad mrad 0.001 rad 333 nmi nmi 1.852 km 334 nmile nmi 1.852 km 335 nmiles nmi 1.852 km 336 oilbarrel bbl 158.9873 L 337 oilbarrels bbl 158.9873 L 338 oz oz 28.349523125 g 339 ozt ozt 31.1034768 g 340 ounce oz 28.349523125 g 341 ounces oz 28.349523125 g 342 pennyweight dwt 1.55517384 g 343 pint pt 473.176473 mL 344 pla tr 6.2831853071796 rad 345 pound lb 0.45359237 kg 346 pounds lb 0.45359237 kg 347 psi psi 6894.757293168 Pa 348 pt pt 473.176473 mL 349 rad rad 57.295779513082 deg 350 radian rad 57.295779513082 deg 351 radians rad 57.295779513082 deg 352 rev tr 6.2831853071796 rad 353 revolution tr 6.2831853071796 rad 354 revolutions tr 6.2831853071796 rad 355 sfeet ft² 0.09290304 m² 356 sfoot ft² 0.09290304 m² 357 sft ft² 0.09290304 m² 358 smi mi² 2.5899881103360 km² 359 smile mi² 2.5899881103360 km² 360 smiles mi² 2.5899881103360 km² 361 syard yd² 0.83612736 m² 362 syards yd² 0.83612736 m² 363 syd yd² 0.83612736 m² 364 syds yd² 0.83612736 m² 365 sqfeet ft² 0.09290304 m² 366 sqfoot ft² 0.09290304 m² 367 sqft ft² 0.09290304 m² 368 sqmi mi² 2.5899881103360 km² 369 sqmile mi² 2.5899881103360 km² 370 sqmiles mi² 2.5899881103360 km² 371 sqyard yd² 0.83612736 m² 372 sqyards yd² 0.83612736 m² 373 sqyd yd² 0.83612736 m² 374 sqyds yd² 0.83612736 m² 375 tb tb 1099511627776 bytes 376 tib tb 1099511627776 bytes 377 ton ton 907.18474 kg 378 tr tr 6.2831853071796 rad 379 troyounce ozt 31.1034768 g 380 troyounces ozt 31.1034768 g 381 troypound lbt 373.2417216 g 382 troypounds lbt 373.2417216 g 383 turn tr 6.2831853071796 rad 384 turns tr 6.2831853071796 rad 385 ukpint ukpt 568.26125 mL 386 ukpt ukpt 568.26125 mL 387 uspint pt 473.176473 mL 388 uspt pt 473.176473 mL 389 w wk 604800 s 390 week wk 604800 s 391 weeks wk 604800 s 392 wk wk 604800 s 393 wks wk 604800 s 394 yard yd 0.9144 m 395 yard2 yd² 0.83612736 m² 396 yards yd 0.9144 m 397 yards2 yd² 0.83612736 m² 398 yd yd 0.9144 m 399 yds yd 0.9144 m 400 yd² yd² 0.83612736 m² 401 yds² yd² 0.83612736 m² 402 EOF