Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

bash

shell

printf

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.

like image 261
Richard Avatar asked Nov 10 '11 10:11

Richard


2 Answers

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.

like image 73
glglgl Avatar answered Sep 23 '22 21:09

glglgl


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.

like image 27
morgant Avatar answered Sep 20 '22 21:09

morgant