De acordo com as Leis 12.965/2014 e 13.709/2018, que regulam o uso da Internet e o tratamento de dados pessoais no Brasil, ao me inscrever na newsletter do portal DICAS-L, autorizo o envio de notificações por e-mail ou outros meios e declaro estar ciente e concordar com seus Termos de Uso e Política de Privacidade.
Colaboração: Julio Cezar Neves
Essa é uma dica longa porque foi uma thread que começou com uma dúvida e
durou diversos dias em uma lista de discussão de Shell. O noso colega de lista
queria fazer preenchimento à esquerda com o símbolo # para fazer bloqueio de
um campo. Para tal, ele fez um for cheio de álgebra, para calcular quantas
voltas daria o loop, já que o tamanho total do campo era 15, mas a quantidade
de algarismos de $Valor
era variável. A minha proposta foi:
$ Valor=123,45
$ Valor=$(printf "%15s\n" $Valor)
$ tr ' ' '#' <<< "$Valor"
#########123,45
Ou seja, o printf
preencheu com espaços completando as 15 posições
predeterminadas e o tr trocou os espaços pelos caracteres de bloqueio
(#). Fique atento: se você não proteger com aspas a variável $Valor, os espaços
em branco inseridos pelo printf
serão perdidos durante a execução do tr.
Mas o printf
possui a opção -v, com a qual podemos indicar o nome de uma
variável que receberá a saída do comando. Assim sendo, obteríamos o mesmo
resultado fazendo:
$ Valor=123,45
$ printf -v Valor "%15s\n" $Valor
$ printf '%s\n' ${Valor// /#}
#########123,45
Isso por si só, já aceleraria bastante, pois evitaria o fork produzido pela
substituição de comandos ($(..)
), mas também usamos uma Expansão de Parâmetros,
onde ${Valor// /#}
que atua como um comando tr
só que
muito mais veloz, ela serve para trocar todos os espaços em branco por #.
Já que falamos nessa Expansão de Parâmetros, olha uma forma legal de se corrigir a máscara de um endereço de hardware (mac address) em maiúsculas ou minúsculas, a seu gosto:
$ Mac=0:13:ce:7:7a:ad $ printf -v Mac "%02x:%02x:%02x:%02x:%02x:%02x" 0x${Mac//:/ 0x} $ echo $Mac 00:13:ce:07:7a:ad $ printf -v Mac "%02X:%02X:%02X:%02X:%02X:%02X" 0x${Mac//:/ 0x} $ echo $Mac 00:13:CE:07:7A:AD
Nesse exemplo, repare o que a Expansão de Parâmetros fez:
$ Mac=0:13:ce:7:7a:ad
$ echo 0x${mac//:/ 0x}
0x0 0x13 0xce 0x7 0x7a 0xad
Agora que todos viraram hexadecimais, foi só meter um printf
para formatar.
Seguindo a discussão da lista de Shell da qual falávamos antes da Expansão de Parâmetros interromper o nosso papo, alguém começou a falar em fazer uma linha
tracejada com 20 caracteres traço (-
) e aí apareceram as seguintes propostas:
$ printf '%s\n' --------------------
for
) para imprimir um hífen (com printf
)
vinte vezes e no final saltar linha (echo
).
$ for ((x = 0; x < 20; x++)); do > printf %s - > done; echo
$ printf -v Espacos %20s
$ echo "${Espacos// /-}"
--------------------
Assim procedendo, a opção -v
jogou a saída do primeiro printf
(vinte
espaços em branco) na variável $Espacos
e a Expansão de Parâmetros substituiu
os espaços em branco por hifens, gerando o resultado desejado.
$ printf "%$(tput cols)s\n" ' ' | tr ' ' -
Nesse caso o tput cols
devolveu a quantidade de colunas da janela e o
printf
, juntamente com o tr
, executou o que desejávamos, como já vimos
nos outros exemplos.
$ Tracos="--------------------------------------\ —----------------------------------------------\ —----------------------------------------------\ —----------------------------------------------" $ printf '%s\n' "${Tracos:0:$(tput cols)}"Primeiramente criei uma linha com muitos hifens (mais do que suficientes para fazer o tracejado que desejamos), em seguida usei uma Expansão de Parâmetros que significa "extraia da variável
$Tracos
uma subcadeia que começa na
posição 0
(início) e tem tput cols
(quantidade de colunas da sessão
corrente) caracteres".
Poderíamos evitar a Expansão de Parâmetros usando o comando cut
, porém
ficaria mais lenta e mais complicada:
$ printf '%s\n' $(cut -c 1-$(tput cols) <<< $Tracos)Nesse caso o
cut
cortou Tracos
desde o 10 caractere (-c 1
) até
(-
) o caractere na posição do tamanho da tela ($(tput cols)
).
De repente um colega mandou a seguinte dica para a nossa lista:
"Só compartilhando uma funçãozinha que fiz aqui para desenhar caixas de mensagem (só funciona para mensagens com uma linha. Se alguém quiser alterar, fique à vontade)"
E nos brindou com esse código:
function DrawBox { string="$*"; tamanho=${#string} tput setaf 4; printf "\e(0\x6c\e(B" for i in $(seq $tamanho) do printf "\e(0\x71\e(B" done printf "\e(0\x6b\e(B\n"; tput sgr0; tput setaf 4; printf "\e(0\x78\e(B" tput setaf 1; tput bold; echo -n $string; tput sgr0 tput setaf 4; printf "\e(0\x78\e(B\n"; tput sgr0; tput setaf 4; printf "\e(0\x6d\e(B" for i in $(seq $tamanho) do printf "\e(0\x71\e(B" done printf "\e(0\x6a\e(B\n"; tput sgr0; }Seu uso seria da seguinte forma:
$ DrawBox Qualquer frase que caiba no terminal
Qualquer frase que caiba no terminal
Só que essa caixa é azul (tput setaf 4
) e as letras são vermelhas, com ênfase
(tput setaf 1; tput bold
).
Mas observe a tabela a seguir. Ela explica os códigos gerados por esse monte de printf
estranho:
O printf |
Produz |
---|---|
\e(0\x6c\e(B |
+ |
\e(0\x71\e(B |
- |
\e(0\x6b\e(B |
+ |
\e(0\x78\e(B |
| |
\e(0\x6d\e(B |
+ |
\e(0\x6a\e(B |
+ |
Como eu tinha acabado de escrever os exemplos que vimos antes e ainda estava com eles na cabeça, sugeri que o for que ele usou para fazer as linhas horizontais fosse trocado; assim, substituiríamos:
for i in $(seq $tamanho) do printf "\e(0\x71\e(B" donePor:
printf -v linha "%${tamanho}s" ' ' printf -v traco "\e(0\x71\e(B" echo -n ${linha// /$traco}Tudo Shell puro, pois o for, o
printf
e o echo
são built-ins, mas como
o printf
dentro do for
seria executado tantas vezes quantos traços
horizontais houvessem, imaginei que a minha solução fosse um pouco mais
veloz e pedi-lhe para testar os tempos de execução, não sem antes apostar
um chope. Ele criou dois scripts, um que executava mil vezes a função que
ele havia proposto e outro que fazia o mesmo com a minha solução. Não vou
dizer qual foi a forma mais veloz, porém adianto que o colega ainda está me
devendo um chope... ;)
O código otimizado ficaria assim:
function DrawBox { string="$*"; tamanho=${#string} tput setaf 4; printf "\e(0\x6c\e(B" printf -v linha "%${tamanho}s" ' ' printf -v traco "\e(0\x71\e(B" echo -n ${linha// /$traco} printf "\e(0\x6b\e(B\n"; tput sgr0; tput setaf 4; printf "\e(0\x78\e(B" tput setaf 1; tput bold; echo -n $string; tput sgr0 tput setaf 4; printf "\e(0\x78\e(B\n"; tput sgr0; tput setaf 4; printf "\e(0\x6d\e(B" printf -v linha "%${tamanho}s" ' ' printf -v traco "\e(0\x71\e(B" echo -n ${linha// /$traco} printf "\e(0\x6a\e(B\n"; tput sgr0; }Então agora, voltando aos nossos exemplos de passar uma linha por todo o terminal, posso sugerir uma outra (e mais bonita) solução:
printf -v linha "%$(tput cols)s" ' ' printf -v traco "\e(0\x71\e(B" echo ${linha// /$traco}
This policy contains information about your privacy. By posting, you are declaring that you understand this policy:
This policy is subject to change at any time and without notice.
These terms and conditions contain rules about posting comments. By submitting a comment, you are declaring that you agree with these rules:
Failure to comply with these rules may result in being banned from submitting further comments.
These terms and conditions are subject to change at any time and without notice.
Comentários