Preenchendo espaços em branco à direita em uma string com outro caractere


12

Eu gostaria de produzir hello worldmais de 20 caracteres.

printf "%-20s :\n\n" 'hello world!!'

# Actual output
hello world!!        :

# Wanted output
hello world!!========:

No entanto, não quero concluir com espaços, mas com " = ". Como faço isso?

Respostas:


9

Resposta atualizada para ser uma solução mais geral. veja também minha outra resposta abaixo usando apenas shell brace expansion e pritnf.

$ str='Hello World!'
$ sed -r ':loop; s/ (=*):$/\1=:/; t loop' <<< "$(printf '%-20s:\n' "$str" )"
Hello World!========:

Como funciona?

isso (=*):$/captura um espaço, um ou mais =que é seguido por dois pontos :no final de sua entrada; fazemos o conjunto =como uma correspondência de grupo e \1será sua referência anterior.

Com :loopnós definimos um rótulo nomeado loope com t loopele pularemos para esse rótulo quando um s/ (=*):$/\1=:/fizer uma substituição bem-sucedida;

Em substituição a \1=:, sempre aumentará o número de se =retornará os dois pontos até o final da string.


1
então você precisa ajustar o sinalizador, dependendo de quantas palavras a entrada possui?
— user1686

@ grrawity Atualizei minha resposta para a solução geral agora.
— αғsнιη

20
filler='===================='
string='foo'

printf '%s\n' "$string${filler:${#string}}"

Dá

foo=================

${#string}é o comprimento do valor $stringe ${filler:${#string}}é a substring $fillerdo deslocamento em ${#string}diante.

A largura total da saída será a largura máxima de $fillerou $string.

A cadeia de preenchimento pode, em sistemas que possui jot, ser criada dinamicamente usando

filler=$( jot -s '' -c 16 '=' '=' )

(para 16 =em uma linha). Os sistemas GNU podem usar seq:

filler=$( seq -s '=' 1 16 | tr -dc '=' )

Outros sistemas podem usar Perl ou alguma outra maneira mais rápida de criar a string dinamicamente.


por que não usar printf para gerar o filtro que está quase disponível em todos os sistemas e a expansão de braçadeiras com cascas como bash/szh?
— αғsнιη

@sddgob Como você faria isso com a printfexpansão + brace bash?
— Kusalananda


17
printf "%.20s:\n\n" "$str========================="

onde %.20sé o formato de truncamento de string


2
Esta é IMHO, uma solução melhor que a minha. Ele precisa apenas de uma breve explicação sobre a string de formato.
— Kusalananda

12

Uma maneira de fazer isso:

printf "====================:\r%s\n\n" 'hello world!!'

6
Ha! Esse é um truque inteligente! No entanto, ele será impresso ====================\rhello world, o que pode ser um problema se o OP precisar armazenar isso e não apenas imprimi-lo na tela.
— terdon

também echo -e '=================\rHello World!!', mas tem o mesmo problema que o @terdon apontou isso.
— αғsнιη

1
@ αғsнιη Apenas se houver echosuporte -e. printfquase sempre é melhor que echo, por muitas razões.
— Satō Katsura

5

Uma abordagem Perl:

$ perl -le '$k="hello world!!"; while(length($k)<20){$k.="=";} print "$k\n"'
hello world!!=======

Ou melhor, @SatoKatsura apontou nos comentários:

perl -le '$k = "hello world!!"; print $k, "=" x (20-length $k), "\n"'

Se você precisar oferecer suporte a caracteres de vários bytes UTF, use:

PERL_UNICODE='AS' perl -le '$k = "hello world!!"; print $k, "=" x (20-length $k), "\n"'

Mesma idéia no shell:

v='hello world!!'; while [ ${#v} -lt 20 ]; do v="$v""="; done; printf '%s\n\n' "$v"

Você não precisa de um loop: perl -le '$k = "hello world!!"; print $k, "=" x (20-length $k), "\n"'. No entanto, essa (e todas as outras soluções postadas até o momento) são interrompidas se caracteres de vários bytes estiverem envolvidos.
— Satō Katsura

@ SatōKatsura ooh, sim, isso é legal! Deveria ter pensado nisso, obrigado. E sim, eu estava pensando em adicionar um aviso de isenção por possível falha nos caracteres de vários bytes UTF, mas imaginei que seria uma complicação desnecessária nesse contexto.
— terdon

Eu acho que perl6pode ter uma maneira de fazê-lo corretamente, mesmo com caracteres de vários bytes. Mas, por outro lado, perl6é irritante de muitas maneiras.
— Satō Katsura

@ SatōKatsura bem, para esse tipo de coisa simples, deve ser o suficiente para definir PERL_UNICODE='AS'. Por exemplo: printf '%s' nóóös | perl -nle 'print length($_)'imprime 8 ("incorreto") enquanto printf '%s' nóóös | PERL_UNICODE='AS' perl -nle 'print length($_)'imprime 5 ("correto").
— terdon

5

Outra maneira é usar apenas o printfcomando e gerar primeiro o padrão de preenchimento de caracteres Shell Brace Expansion(Você pode finalizar com um número ≥ da área de formatação em que deseja imprimir {1..end}) e obter apenas cada primeiro caractere %.1sque seja =s e depois imprimir apenas os primeiros 20 caracteres área disso %.20s. Essa é a maneira melhor de repetir caracteres / palavra em vez de duplicá-los.

printf '%.20s:\n' "$str$(printf '%.1s' ={1..20})"
Hello World!!=======:

Explicações:

Normalmente, como expansão de chaves , expansão de shell da {1..20}seguinte forma, se as imprimirmos.

printf '%s ' {1..20}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 

Portanto, com a adição de um sinal de igual ={1..20}, o shell se expandirá da seguinte forma.

printf '%s ' ={1..20}
=1 =2 =3 =4 =5 =6 =7 =8 =9 =10 =11 =12 =13 =14 =15 =16 =17 =18 =19 =20 

E com o printf '%.1s'que realmente significa printf '%WIDE.LENGTH', estamos imprimindo apenas um COMPRIMENTO daqueles acima com o padrão 1 WIDE . assim resultará =apenas se 20 vezes se repetiu.

Agora, com printf '%.20s:\n'estamos imprimindo apenas o comprimento de 20 $stre se o comprimento for $str<20, o restante levará de =s gerados para preencher em vez de espaços.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.