6

I want to add two hexadecimal variables in a bash script. I want them to start as hex and end in hex, not decimal.

What I've come up with so far is a bit of a round about hack. Is there a better or more elegant solution?

BASE=0xA000

OFFSET=0x1000

NEW_BASE=$(( $BASE + $OFFSET ))

NEW_BASE=`printf "0x%X\n" $NEW_BASE`

echo $NEW_BASE

0xB000
6

Yes, in bash, printf is the only builtin way to reformat a number in a different base and only bases 8, 10 and 16 are supported.

In bash (contrary to shells like ksh93 or fish), using command substitution implies forking a subshell. You can use printf -v here to avoid the subshell (also available in recent versions of zsh for print and printf (print -f) which also supports printing into arrays):

printf -v NEWBASE '%#X' "$((BASE + OFFSET))"

(in bash, contrary to zsh, $((...)) is subject to word splitting, so needs quoted to avoid the dependency on $IFS).

In zsh, you can specify the expansion base as part of the arithmetic expansion syntax (bases 2 to 36):

$ echo $(([#16] 0xff + 0xff))
16#1FE
$ echo $(([##16] 0xff + 0xff))
1FE
$ echo 0x$(([##16] 0xff + 0xff))
0x1FE
$ echo $(([##2] 0xff + 0xff))
111111110

With ksh and zsh, you can also force the expansion of an integer variable to be in a specific base with:

typeset -i 16 NEWBASE

The expansion will be in the 16#1FE form. ksh93 supports bases up to 64, zsh and mksh up to 36.

ksh93's printf builtin supports outputting number in arbitrary bases as well with or without the n# prefix:

$ printf '%..2d\n' 0x1FE
111111110
$ printf '%#..2d\n' 0x1FE
2#111111110

In ksh93, var=$(printf...) doesn't fork a subshell so is as efficient as bash's printf -v.

  • I wasn't aware of the -v <variable assignment syntax> NIIICE!!! Thank you :) – David Aubin Jan 6 '18 at 3:55
6

I would just simplify your script as:

printf "0x%X\n" $((0xA000 + 0x1000))
  • 1
    or printf -v newbase "0x%X\n" $((0xA000 + 0x1000)); echo $newbase – Cyrus Jan 5 '18 at 21:02
  • I like the one liner :) Any way to get rid of the subshell (back ticks) for variable assignment? – David Aubin Jan 5 '18 at 21:03
  • 1
    Thers's no subshell. $((...)) is ugly addition syntax. With bash this is also possible: $[0xA000 + 0x1000] – Cyrus Jan 5 '18 at 21:04
  • @Cyrus It is possible, but it is a deprecated format. – Rui F Ribeiro Jan 5 '18 at 21:07
  • 1
    I need the final result assigned to a variable though. Hence the subshell with the printf wrapped in back ticks. Still, this is the best yet :) – David Aubin Jan 6 '18 at 3:50
2

In GNU or modern BSD dc you can do this like so:

echo A000 1000 | dc -e '16o16i?+p'

16o sets the output base. 16i sets the input base. The ? reads in a line from standard input, which in this case pushes two numbers onto the stack. + adds them. p prints the top of the stack (the answer).

1

within bash it seems to be the good method. you can also call tools like bc/dc (depending on your preferences) ... echo 'obase=30; 123456' | bc

but I prefer the bash method In my point of view you 're doing well

  • Thank you :) But that shell to printf is what makes me twitch. Would be nice to not need to subshell out to get a formatted result. – David Aubin Jan 5 '18 at 20:52
  • 1
    @DavidAubin Note that printf is a bash built-in and probably much faster that any real command like bc. – rudimeier Jan 5 '18 at 21:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.