printf in bash: "09" and "08" are invalid numbers, "07" and "06" are fine

Numbers beginning with "0" are treated as octal (i.e. base-8). Therefore, "8" and "9" aren't valid digits.

See http://www.gnu.org/software/bash/manual/bashref.html#Shell-Arithmetic.

This behaviour is inherited from languages like C.


If you have your "09" in a variable, you can do

a="09"
echo "$a"
echo "${a#0}"
printf "%04d" "${a#0}"

Why does this help? Well, a number literal starting with 0 but having no x at the 2nd place is interpreted as octal value.

Octal value only have the digits 0..7, 8 and 9 are unknown.

"${a#0}" strips one leading 0. The resulting value can be fed to printf then, which prints it appropriately, with 0 prefixed, in 4 digits.

If you have to expect that you get values such as "009", things get more complicated as you'll have to use a loop which eliminates all excess 0s at the start, or an extglob expression as mentioned in the comments.


Bash's numeric arithmetic evaluation syntax (( ... )) can convert to base 10 (therefor ensuring correct interpretation) with the following syntax: (( 10#$var )). Or, in the case of a raw number: (( 10#08 )). Very simple & clean and can be used anywhere you're sure the base should be 10, but can't guarantee a leading zero won't be included.

So, in your example it would be as follows:

printf "%04d\n" $(( 10#09 ))
printf "%04d\n" $(( 10#08 ))
printf "%04d\n" $(( 10#07 ))
printf "%04d\n" $(( 10#06 ))

Producing the following output:

0009
0008
0007
0006

With this syntax, since you're then working with the value of the variable instead of variable itself, incrementors (( var++ )) & decrementors (( var-- )) won't work, but can still be relatively cleanly implemented as var=$(( 10#var + 1 )) and var=$(( 10#var - 1 )), respectively.

I first encountered this solution here, but this answer to a similar Stack Overflow question also demonstrates it.

Tags:

Shell

Printf

Bash