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