Como @Gnouc já notou, você já está no meio do caminho:
line='some chars'
printf '%0'$((20-${#line}))'d%s\n' 0 "$line"
###OUTPUT###
0000000000some chars
Claro, você ainda terá um 0, mesmo que a linha tenha mais de 20 caracteres, e isso é apenas se printf não falhar com um erro porque não funciona corretamente manipule o - dash em sua string de formato (que aparentemente é o caso mais provável) . Ainda assim, isso é facilmente manipulado:
printf "%.$((((l=20-${#line})>0)*l))d%s\n" 0 "$line"
Essa string de formato usa o sinalizador printf ' %. precision para zerar seu argumento decimal no comprimento indicado ou - no caso de um %.0d null ou %.d precisão vazia - para truncar os 0s iniciais para o número indicado. Assim:
printf '#%.0d%c%.5d' 0000000 ':separator' 4
#:00004
(nota: o formato %c imprime o primeiro caractere do seu argumento associado)
A expressão aritmética $(( )) define primeiro a variável $l para 20 menos ${#line} , em seguida, avalia para 0 ou 1 se $l for menor ou maior que 0, respectivamente, e última multiplicação por% código%. Então quando 20 - $l for menor que zero você faz ${#line} mas quando é maior você faz $((0*l)) .
Desta forma, você sempre imprime apenas os zeros à esquerda contados na diferença entre 20 e $((1*l)) . Assim:
line='some chars'
printf "#%.$((((l=20-${#line})>0)*l))d%s\n" 0 "$line"
#0000000000some chars
E ...
line='some chars'
printf "#%.$((((l=4-${#line})>0)*l))d%s\n" 0 "$line"
#some chars
E você também pode usar o mesmo formulário ${#line} precision para o campo %. type, como:
line='some chars'
printf "#%.$((((l=4-${#line})>0)*l))d%.4s\n" 0 "$line"
#some
... para truncamento, conforme desejado.
Aqui está em um loop - o seguinte faz uma iteração cinco vezes:
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
É importante observar que nenhum dos zeros mostrados é incluído no valor de qualquer variável do shell - e muito menos s - e são gerados automaticamente somente por $line , conforme indicado pela string de formatação.
Aqui está o mesmo loop com uma precisão adicional printf :
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
Outra coisa que você pode fazer com isso é muito divertido. Quando você combina a capacidade de %.20s de gerar dinamicamente 0s com printf , você pode ter qualquer string desejada quantas vezes quiser, como:
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