Adicione os caracteres iniciais na frente da string usando printf ou echo

6

Como posso adicionar caracteres iniciais para preencher uma string com um determinado comprimento?

Suponha que eu queira adicionar zeros na frente de uma string, se a string for menor que 20 caracteres:

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

No entanto, isso preenche a string com espaços em branco iniciais, mas não qualquer outra coisa, por exemplo, zeros.

A string pode ser uma string aleatória contendo números e outros caracteres.

Eu não quero dividir a string em número + restante para printf '%020i' $number ou printf '%020d $number' .

Minha saída atual é:

         shortstring

Minha saída desejada é:

000000000shortstring
    
por polym 18.07.2014 / 11:17

5 respostas

1

Se você puder usar perl :

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

Para mais geral:

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

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

Nota

Algumas plataformas podem ser formatadas com zero, pelo menos em AIX . Com GNU gcc , você terá um aviso ao compilar:

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

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

Então:

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

Se você não quiser usar perl , tente:

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

Essa abordagem não pode ser manipulada quando o tamanho da string for maior que 20. Você pode usar a resposta @mikeserv para lidar com isso ou usar uma verificação antes de printf .

    
por 18.07.2014 / 11:29
3

%s é uma especificação de formatação de string. O preenchimento com zero é apenas definido para existir para conversões numéricas :

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.

Se o seu $line for um número, use:

printf '%020i\n' $line

para obter o resultado desejado (ou um dos outros especificadores de formato numérico). Se o seu valor é mais complicado, mas começa com um número, você pode usar:

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

para combinar um número e uma string juntos de uma só vez.

    
por 18.07.2014 / 11:27
3

Basta fazer o preenchimento manualmente. Eu não acho que haja uma solução mais direta se você mantiver as ferramentas POSIX.

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

ou

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

No zsh, claro, há uma sintaxe para isso. Ao contrário dos snippets acima, este trunca a string se tiver mais de 20 caracteres. Se você não quiser truncamento, pode acrescentar o final da string.

print -r ${(l:20::0:)line}
print -r ${(l:20::0:)line}${line:20}
    
por 19.07.2014 / 01:31
3

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
    
por 19.07.2014 / 08:58
1

Um printf separado pode fazer o 0 preenchendo com %0Xd quando necessário.

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
    
por 21.07.2014 / 11:09