Explicação necessária sobre como eu posso repetir um caractere no shell POSIX

8

A resposta a seguir no Stack Overflow,

Como posso repetir um caractere no bash?

impõe uma maneira plausível de POSIX - repetindo apenas um único caractere, da seguinte maneira. Neste exemplo, vamos usar o sinal de igual 100 vezes:

printf %100s | tr " " "="

Meu problema é que eu não entendo como isso funciona e eu preferiria uma explicação direta. Por favor, evite comentários como ler o manual , eu o fiz e, como não sou inteligente, estou fazendo esta pergunta como nunca usei tr, nem vi uma printfdeclaração dessas .

LinuxSecurityFreak
fonte

Respostas:

13

Em resumo, printf %100simprimirá 100 espaços e tr " " "="os converterá em sinais de igual, efetivamente imprimindo 100 sinais de igual.

Dividindo:


printfé um shell embutido. Normalmente, são necessários dois ou mais argumentos, o primeiro dos quais é uma "sequência de formato" e o restante será usado para preencher espaços reservados nessa sequência de formato. Quando esse modelo estiver totalmente preenchido, ele imprimirá o resultado. Se houver mais argumentos, ele será iniciado novamente, preenchendo mais argumentos e imprimindo a sequência resultante.

A string de formato usada para obter printfespecificações de formato, que começam %e terminam com uma única letra, %dsignifica um número inteiro (usando a base decimal, portanto "d"), %fsignifica um número de ponto flutuante e %suma string de caracteres. Caracteres que não sejam letras após %são modificadores para a especificação de formato e, em particular, números são usados ​​para especificar o comprimento solicitado do campo na saída. Assim %100s, formatará a string para ter pelo menos 100 caracteres, preenchê-la-á com espaços e mantê-la alinhada corretamente (em outras palavras, adicione espaços no início da string).

Se passado um argumento extra, ele seria usado para esse %scampo, por exemplo printf %100s abc, imprimirá 97 espaços (para chegar ao total de 100, considerando os 3 em "abc") seguidos pela sequência real "abc". Mas se nenhum argumento for fornecido, a especificação de formato será preenchida com um argumento vazio ou nulo (que é uma string vazia para %s, seria 0 para %detc.). Isso é o mesmo que se uma string vazia fosse passada, como printf %100s ''. O resultado final é que apenas o preenchimento de 100 caracteres é impresso.

Então, juntando tudo, printf %100sresulta em 100 espaços impressos.


Agora tré uma ferramenta para converter caracteres da entrada para a saída. Ele usa dois argumentos, SET1 e SET2, cada um conjunto de caracteres e, em seguida, converte o primeiro caractere de SET1 no primeiro de SET2, o segundo caractere de SET1 no segundo de SET2 e assim por diante. trlê sua entrada de stdin e a grava de volta em stdout (por isso é muito útil em pipelines como o acima.) trsempre traduzirá todas as ocorrências desse caractere em uma determinada string.

Por exemplo, tr aeiou 12345converterá vogais em minúsculas nos números de 1 a 5 nessa ordem, portanto converterá "enfileiramento" em "q52523ng", por exemplo. Você também pode passar intervalos de caracteres, como tr a-z A-Ztransformar qualquer letra minúscula na maiúscula correspondente.

Assim, tr " " "="é simplesmente traduzir espaços em sinais de igual em toda a cadeia. O primeiro espaço precisa ser citado para ser reconhecido como um argumento. Na =verdade, não precisa ser citado, mas isso não faz mal. tr " " =teria funcionado da mesma maneira.


Juntando tudo, imprima 100 espaços e depois traduza cada um deles em sinais de igual.

Espero que isso explique com detalhes suficientes, mas se ainda houver algo que você não entende, deixe um comentário e tentarei abordar isso.

filbranden
fonte
Apenas verificando, seria o seguinte seja mais sintaticamente correto ?:printf '%100s' ' ' | tr " " "="
LinuxSecurityFreak
2
@Vlastimil Na verdade printf '%100s' '', com uma string vazia ... Atualizei a resposta para incluir isso. Nesse caso em particular, uma string vazia ou um único espaço não faria diferença, mas você pode ver uma diferença em printf '%sx\n', que é a mesma coisa printf '%sx\n' ''mas diferente de printf '%sx\n' ' '. Espero que ajude!
filbranden
1
+1 para menção que tropera em conjuntos de caracteres. Isso geralmente é deixado de fora.
Sergiy Kolodyazhnyy 5/01/19
11

O printfcomando usa seu primeiro argumento como um formato para imprimir seus argumentos subsequentes. printf %100simprime seus argumentos preenchidos com 100 caracteres de largura, usando espaços (à esquerda). Como não há argumento fornecido para formatar, ele formata a sequência vazia uma vez e gera 100 espaços. Você pode ver isso:

$ printf %100s | hexdump -C
00000000  20 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |                |
*
00000064

(20 é o hexágono para um espaço; *significa a linha anterior repetida)

As seqüências de formato usam aproximadamente os Xprintfespecificadores C :, %uma largura opcional para ajustar o valor formatado e um tipo de formato a ser usado. sé a formatação de strings, e as strings são preenchidas com espaços à esquerda por padrão. Pode haver vários formatos ou outras partes literais: printf "a%10sb\n" helloimpressões

 a         xb.

trsubstitui os caracteres selecionados em sua entrada padrão por substituições selecionadas e imprime o resultado em sua saída padrão. tr " " "="possui um único caractere a ser substituído - um espaço - e um único caractere para substituí-lo por - um sinal de igual. Assim, transforma todo espaço em sua entrada em um =e deixa o restante inalterado. Você pode tentar isso também:

$ tr " " "="
hello world
hello=world

(Eu digitei o "olá mundo")

Você pode ter várias substituições: tr abc deftransforma a em d, b em e, c em f e deixa o restante inalterado. Aqui está apenas um caractere, já que era isso que printfpoderia gerar mais barato.

O canal |faz com que a saída do comando à esquerda, printf %100sseja usada como entrada para o comando à direita tr " " "=",. Ou seja, cem espaços consecutivos são atribuídos tre cada um deles é substituído por um =, com a nova sequência impressa.

printf %100s | tr " " "="
====================================================================================================
Michael Homer
fonte
Apenas verificando, seria o seguinte seja mais sintaticamente correto ?:printf '%100s' ' ' | tr " " "="
LinuxSecurityFreak
1
Formatar um espaço e preenchê-lo com espaços dará a mesma saída que formatar uma string vazia e preenchê-lo com espaços, mas não é estruturalmente equivalente. Talvez seja mais "correto" em termos de clareza sobre o que está acontecendo, mas não sintaticamente, e são comandos diferentes.
Michael Homer