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

This is my bash script - I just want to left-pad a set of numbers with zeroes:

printf "%04d" "09"
printf "%04d" "08"
printf "%04d" "07"
printf "%04d" "06"

Output:

./rename.sh: line 3: printf: 09: invalid number 
0000
./rename.sh: line 4: printf: 08: invalid number 
0000 
0007
0006

What...?

Only 09 and 08 are causing the problem: every other number in my sequence seems to be OK.


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.


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.


Floating point is handled differently:

printf "%04.f" "009"

This gives the correct output, without dealing with any fancy bashisms (per @Oli Charlesworth's answer, 0*** is treated as octal, but I believe that Bash ignores octal/hex identifiers for floating-point numbers)