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 0
s at the start, or an extglob
expression as mentioned in the comments.
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.
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.
Just to add to Oli's answer, in order to pad a number with zeroes it is enough to put a 0
after the %
, as you did:
printf "%04d" "9"
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)
Adding *
makes shell parameter expansion matching greedy (see, for example, Shell Tipps: use internal string handling)!
# strip leading 0s
- a="009"; echo ${a##0}
+ a="009"; echo ${a##*0}