#!/bin/bash # baseconv - convert a number between numeral bases (binary, octal, decimal, hexadecimal) # # Usage: # baseconv # baseconv -h # # Options: # -h, --help Show this help message _baseconv() ( local SCRIPT_NAME; SCRIPT_NAME="$(basename "${BASH_SOURCE[0]}")" case "${BASH_SOURCE[0]}" in /dev/*|/proc/*) SCRIPT_NAME="" ;; esac case "$SCRIPT_NAME" in ""|bash|sh|zsh|dash) SCRIPT_NAME="baseconv" ;; esac _error() { echo "[ERR][$SCRIPT_NAME] $*" >&2; } _show_help() { local s; [ -t 1 ] && s=$'\033[4m' local r; [ -t 1 ] && r=$'\033[24m' cat <, , and . Run \`$SCRIPT_NAME -h\` for usage" return 2 fi if [ "${#args[@]}" -gt 3 ]; then _error "Too many arguments (expected 3, got ${#args[@]}). Run \`$SCRIPT_NAME -h\` for usage" return 2 fi # Dep check before any external-tool use (tr, grep) so a missing bc is reported as such if ! command -v bc >/dev/null 2>&1; then _error "bc is required" return 3 fi local from_in="${args[0]}" local to_in="${args[1]}" local num="${args[2]}" local in_base if ! in_base="$(_resolve_base "$(printf '%s' "$from_in" | tr '[:upper:]' '[:lower:]')")"; then _error "Invalid base '$from_in' (valid: bin|b|2, oct|o|8, dec|d|10, hex|h|x|16). Run \`$SCRIPT_NAME -h\` for usage" return 2 fi local out_base if ! out_base="$(_resolve_base "$(printf '%s' "$to_in" | tr '[:upper:]' '[:lower:]')")"; then _error "Invalid base '$to_in' (valid: bin|b|2, oct|o|8, dec|d|10, hex|h|x|16). Run \`$SCRIPT_NAME -h\` for usage" return 2 fi # bc requires uppercase hex digits, and case-insensitive input is the friendlier default local up_num; up_num="$(printf '%s' "$num" | tr '[:lower:]' '[:upper:]')" # Validate digits against the input base so bc never sees a malformed number local valid_pat case "$in_base" in 2) valid_pat='^[01]+$' ;; 8) valid_pat='^[0-7]+$' ;; 10) valid_pat='^[0-9]+$' ;; 16) valid_pat='^[0-9A-F]+$' ;; esac if ! printf '%s' "$up_num" | grep -Eq "$valid_pat"; then _error "Invalid number '$num' for base $in_base. Run \`$SCRIPT_NAME -h\` for usage" return 2 fi # Set obase before ibase so the obase literal is read in base 10 instead of the new ibase echo "obase=$out_base; ibase=$in_base; $up_num" | bc ) _baseconv "$@" __baseconv_rc=$? unset -f _baseconv if [ -n "${BASH_SOURCE[0]}" ] && [ "${BASH_SOURCE[0]}" != "$0" ]; then eval "unset __baseconv_rc; return $__baseconv_rc" fi eval "unset __baseconv_rc; exit $__baseconv_rc"