Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

How can I add leading characters to fill a string to a certain length?

Assume I want to add zeroes infront of a string, if said string is shorter than 20 characters:

printf '%020s\n' "$line"

However, this fills the string with leading whitespaces, but not anything else e.g. zeroes.

The string can be a random string containing numbers and other characters.

I don't want to split the string into number + rest for printf '%020i' $number or printf '%020d $number'.

My current output is:

         shortstring

My desired output is:

000000000shortstring
share|improve this question

5 Answers 5

up vote 2 down vote accepted

If you can use perl:

$ perl -e 'print sprintf "%020s\n","shortstring"'
000000000shortstring

For more general:

$ perl -e 'print sprintf "%020s\n",shift' shortstring
000000000shortstring

$ perl -e 'print sprintf "%020s\n", $_ for @ARGV' shortstring qwerty
000000000shortstring
00000000000000qwerty

Note

Some platform can be formated output string with leading zero, at least in AIX. With GNU gcc, you will have a warning when you compile:

$ cat test.c
#include <stdio.h>

int main() {
    printf("%08s\n", "qwerty");
    return 0;
}

Then:

$ gcc -Wall test.c
test.c: In function ‘main’:
test.c:4:5: warning: '0' flag used with ‘%s’ gnu_printf format [-Wformat]

If you don't want to use perl, you can try:

$ line=shortstring
$ printf "%0$((20-${#line}))d%s"  0  $line
000000000shortstring

This approach can not handle when string length greater than 20. You can use @mikeserv answer to handle that or using a check before printf.

share|improve this answer
    
Ok that looks good. Is it possible to give this perl script a set environment variable? I need perl to evaluate $line. –  polym Jul 18 at 9:32
    
@polym: see my updated answer. –  Gnouc Jul 18 at 9:37
    
Cool thanks, it worked! :) That is real command line magic haha. –  polym Jul 18 at 9:37
    
Thanks, man. Sorry again for not getting to the bottom of your answer before posting my own, but I just suck so bad at perl that reading through it hurt my feelings... –  mikeserv Jul 19 at 7:59

%s is a string formatting specification. Zero padding is only defined to exist for numeric conversions:

0 For d, i, o, u, x, X, a, A, e, E, f, F, g, and G conversion specifiers, leading zeros (following any indication of sign or base) are used to pad to the field width ... For other conversions, the behavior is undefined.

If your $line is a number, use:

printf '%020i\n' $line

to get the result you want (or one of the other numeric format specifiers). If your value is more complicated but starts with a number, you can use:

printf '%020i %s\n' $num $restofline

to combine a number and a string together at once.

share|improve this answer
    
Hmm this doesn't quite help me. I need $line in a whole and don't want to split it into $num and $restofline. $line is a string that can contain numbers only, but often contains non-numerals. –  polym Jul 18 at 9:33

Just do the padding manually. I don't think there's a more straightforward solution if you stick to POSIX tools.

while [ ${#line} -lt 20 ]; do
  line=0$line
done

or

n=${#line}
while [ $n -lt 20 ]; do
  printf '0'
  n=$((n-1))
done
printf "$line\n"

In zsh, of course, there's syntax for that. Unlike the snippets above, this one truncates the string if it's longer than 20 characters. If you don't want truncation, you can append the tail of the string.

print -r ${(l:20::0:)line}
print -r ${(l:20::0:)line}${line:20}
share|improve this answer

As @Gnouc has already noted, you're halfway there already:

line='some chars'
printf '%0'$((20-${#line}))'d%s\n' 0 "$line"

###OUTPUT###
0000000000some chars

Of course, then you still wind up with a 0 even if line is longer than 20 chars, and that's only if printf doesn't fail with an error because it doesn't properly handle the -dash in its format string (which is apparently the more likely case). Still, that is easily handled:

printf "%.$((((l=20-${#line})>0)*l))d%s\n" 0 "$line"

That format string uses printf's %.precision flag to either zero pad its decimal argument to the indicated length or - in the event of a %.0d null or %.d empty precision - to otherwise truncate any leading 0s to the number indicated. Like this:

printf '#%.0d%c%.5d' 0000000 ':separator' 4 
#:00004

(note: the %c format prints the first character of its associated argument)

The $((arithmetic expression)) first sets the var $l to 20 minus ${#line} then evaluates to 0 or 1 if $l is less than or greater than 0 respectively, and last multiplies that by $l. So when 20 - ${#line} is less than zero you do $((0*l)) but when it is greater you do $((1*l)).

In this way you always print only as many leading zeroes as are counted in the difference between 20 and ${#line}. Like this:

line='some chars'
printf "#%.$((((l=20-${#line})>0)*l))d%s\n" 0 "$line"
#0000000000some chars

And...

line='some chars'
printf "#%.$((((l=4-${#line})>0)*l))d%s\n" 0 "$line"
#some chars

And you can use the same %.precision form for the s type field as well, like:

line='some chars'
printf "#%.$((((l=4-${#line})>0)*l))d%.4s\n" 0 "$line"
#some

...for truncation as desired.

Here it is in a loop - the following iterates 5 times:

line=
until line=fivec$line; [ $((l)) -lt 0 ] 
do  printf "#%.$((((l=20-${#line})>0)*l))d%s\n" 0 "$line"
done    
#000000000000000fivec
#0000000000fivecfivec
#00000fivecfivecfivec
#fivecfivecfivecfivec
#fivecfivecfivecfivecfivec

It's important to note that none of the zeroes shown are ever included in the value of any shell variable at all - let alone $line - and are only generated automatically by printf as indicated by its format string.

Here's the same loop with an added precision %.20s:

line=
until line=fivec$line; [ $((l)) -lt 0 ] 
do  printf "#%.$((((l=20-${#line})>0)*l))d%.20s\n" 0 "$line"
done    
#000000000000000fivec
#0000000000fivecfivec
#00000fivecfivecfivec
#fivecfivecfivecfivec
#fivecfivecfivecfivec

Another thing you can do with this is pretty fun. When you combine printf's capability of dynamically generating 0's with $IFS you can have any string you want as many times as you want, like:

IFS=0 ; printf '10 lines\n%s' $(printf %010d) ; unset IFS 

###OUTPUT###
10 lines
10 lines
10 lines
10 lines
10 lines
10 lines
10 lines
10 lines
10 lines
10 lines
share|improve this answer
    
+1, good point, I didn't think a bout that. –  Gnouc Jul 19 at 7:08

A separate printf can do the 0 padding with %0Xd when needed.

zero_pad(){
  # zero_pad <string> <length>
  [ ${#1} -lt $2 ] && printf "%0$(($2-${#1}))d" ''
  printf "%s\n" "$1"
}

.

$ zero_pad "" 5
00000

$ zero_pad "over" 5
0over

$ zero_pad "under" 4
under

$ zero_pad "exact" 5
exact

$ zero_pad " space" 7
0 space
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.