Sometimes we need hexadecimal numbers. Sometimes we need to loop over them. And sometimes we need to do that in Bash.

Chevrolet El Camino?

We are talking about Bash, so it only takes one line to do it. For example, to go from 8 to 0xe (not inclusive), and print the decimal value and their hex number:

for ((i = 8; i < $(printf %d 0xe); ++i)); do j=$(printf "%x" $i); echo -e "$i:\t $j"; done
8:       8
9:       9
10:      a
11:      b
12:      c
13:      d

Now, let’s see bit by bit what is this one-liner. The same code, in multiple lines, for educational purposes only:

for ((i = 8; i < $(printf "%d" 0xe); ++i)); do 
	j=$(printf "%x" $i)
	echo -e "$i:\t $j"

The first line is a C-style for loop. The loop variable, i, starts from 8 and the loop goes on while i < $(printf "%d" 0xe) is true. The loop variable is incremented (++i, C-style pre-increment) at the end of each iteration.

The printf "%d" 0xe is the cool trick: it takes the string 0xe and generates another string using the formatter %d. This means: print 0xe as an integer. This way the loop starts at 8 and ends at 14 (0xe), not inclusive. The printf is executed using command substitution, so its value “replaces” the command. This is what “tricks” the for loop into starting from 0 and ending at 13.

As the loop is not inclusive (the condition is i < 14), the last iteration happens with i=13.

The second printf does the opposite: takes the variable i, prints as hexadecimal (formatter %x is for hexadecimal numbers) and “saves” it as the variable j. The echo statement prints i, the decimal value, and j, the corresponding hexadecimal.

All one needs when coding in Bash is one line.

And just for fun, here’s the equivalent Python code:

for i in range(0, 0xE):
    print(f"{i}:   {i:x}")

But why use Python when we can suffer with Bash?