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.


Expansão de Parâmetros - Parte 4

Colaboração: Julio Cezar Neves

Data de Publicação: 29 de setembro de 2021

Antes da dica de hoje, uma mensagem importante.

Às 20h teremos uma aula ao vivo com o tema Shell Linux: Ao Infinito e Além.

A aula será transmitida pelo Youtube.

Para assistir, clique aqui.

Um forte abraço e até lá!

Rubens


Nas Expansões de Parâmetros que trabalham com texto, sempre podemos usar também os coringas de expansão de nomes de arquivos (globbing) e a extensão desses padrões (extglob) que vimos no início desta série de arquivos.

Excluindo à esquerda (#)

Nos exemplos a seguir usaremos:

Arq=/usr/share/icons/gnome/scalable/places/debina-swirl.svg
Magica=Abracadabra
Dt=27/09/2021

Para ficar com MÊS/ANO:

$ echo ${Dt#*/}	# Tudo (*) até a barra
09/2021 
$ echo ${Dt#???}	# 3 caracteres (???)
09/2021
$ echo ${Dt#[0-9][0-9]/}	# 2 algarismos ([0-9][0-9]) e a barra
09/2021 
$ echo ${Dt#+([0-9])/}	# No 1º artigo falo sobre extglb
09/2021 

Excluindo o caminho completo do diretório, ou seja, tudo até a última barra (/), seria o mesmo que o comando basename, só que muuuuuito mais rápido

$ BaseName=${Arq##*/}; echo $BaseName
debina-swirl.svg

Excluindo à direita (%)

Excluindo o nome do arquivo, ou seja, tudo a partir da última barra (/), seria o mesmo que o comando dirname, só que muuuuuito mais rápido.

$ DirName=${Arq%/*}; echo $DirName
/usr/share/icons/gnome/scalable/places

Só para você ver que como as Expansões de Parâmetros são velozes. Vou executar 2000 vezes o comando dirname e fazer o mesmo usando Expansões de Parâmetros. Veja:

$ time for ((i=1; i<2000; i++)); { dirname $Arq > /dev/null; }
real	0m2.193s
user	0m0.288s
sys	0m0.364s

$ time for ((i=1; i<2000; i++)); { : ${Arq%/*}; }
real	0m0.025s
user	0m0.024s
sys	0m0.000s

É brincadeira? Pelas minhas contas deu 90 vezes mais rápido. Experimente fazer outras comparações do tipo comandos vs Expansões de Parâmetros.

E aí você me pergunta:

— Tá tudo muito bom, mas me diga: Já sei como pegar o dia (${Dt%%/*}) e como pegar ano (${Dt##*/}). Mas e para ficar só com o mês?

— Então presta atenção que lá vem uma carga pesada de macetes

$ : ${Dt#*/}; echo $_
09/2021
$ echo O Mês é ${_%/*}; echo Agora '$_' ficou com $_
O Mês é 09

Agora $_ ficou com 09

Ou seja, o comando mais rápido que existe é o : (que é o mesmo que o true - experimente também true ${Dt#*/}) e a saída de qualquer Expansão de Parâmetros é armazenada na variável $_ que pode ser usada para concatenar Expansões de Parâmetros.

Agora experimente você fazer aquele loop de 2000 vezes com o comando

cut -f2 -d/ <<< $Dt >/dev/null

e comparar com as duas Expansões de Parâmetros

: ${Dt#*/}; : ${_%/*}

Você terá uma surpresa com os tempos ...

Trocando textos (/)

Idêntico aos outros, uma barra (/) troca a primeira ocorrência, duas trocam todas:

$ echo ${Magica/a/X}
AbrXcadabra
$ echo ${Magica//a/X}
AbrXcXdXbrX
$ echo ${Magica//[Aa]/X}
XbrXcXdXbrX

Agora vou dar uma aprofundada pelo campo da extglob. Suponha que seu programa leia uma resposta para a variável $Resp, ela poderá conter s, sim, y ou yes em caixa alta ou baixa. Para criticar o conteúdo de $Resp, podemos fazer:

$ Resp=sIm; : ${Resp^^}	# Tudo em Maiúscula
$ Resp=${_/@(S|SIM|YES|Y)/S}	# Se for uma das aceitas, vira S
$ echo $Resp
S
$ Resp=n; : ${Resp^^}
$ Resp=${_/@(S|SIM|YES|Y)/S}
$ echo $Resp
N

Agora basta testar se $Resp não é igual a S ...

Esse foi um exemplo didático, pois o novo comando test ([[ ... ]]) também aceita extglob e Expansões de Parâmetros, então ficaria melhor se fosse:

[[ ${Resp^} == @(S|SIM|YES|Y)/S} ]] && echo Resposta certa

Para cortar cadeias (:)

Isso substitui o cut -c com muita vantagem. Para cortar $Var1 da posição 2 (origem zero) com 3 caracteres, faça:

$ Var1=cadeia
$ Seq=123456
$ echo ${Seq:2:3}      # Da posição 2 (origem zero) com 3 caracteres
345 
$ echo ${Var1:2:3}     # Idem
dei 
$ echo ${Var1:2}       # Da posição 2 até o fim
deia 
$ echo ${Var1:2: -1}   # Da posição 2 até uma posição antes do fim
dei 
$ echo ${Var1: -4}     # Da 4a posição antes do fim até o fim
 deia 
$ echo ${Var1: -4: -1} # Da 4a posição antes do fim até uma antes
dei 

Para determinar o tamanho (#)

Depois dessa, nunca mais use wc -c.

$ echo $Magica         # Só para lembrar
Abracadabra
$ echo ${#Magica}
11

Para fazer Indireção (!)

O que chamo de indireção é quando uma variável armazena o nome de outra e é dessa última que nos interessa o valor. Ou seja, a primeira variável aponta para a que tem o valor que nos interessa.

Exemplo:

$ Var1=Var2                 # Var 1 apontando para Var2
$ Var2=Valor_que_desejamos

O que chamo de indireção é quando quero pegar um valor indiretamente, isto é, estamos a fim de Valor_que_desejamos, mas só temos como referência Var1. Isso se faz da seguinte forma:

$ echo ${!Var1}
Valor_que_desejamos

Um exemplo mais palpável. Podemos saber qual foi o último parâmetro passado fazendo o seguinte:

$ echo ${!#}

Onde # (que dentro de uma Expansão de Parâmetros é o mesmo que $#) significa o total de parâmetros passados. Então suponha que tenham passado 3 parâmetros, quando então $# teria 3 e eu quero listar o último deles, ou seja, o $3. Então essa Expansão de Parâmetros lista o valor da variável que está sendo apontada por $#, isto é, $3.

$ set {a..f}      # Expande para a b c d e f
$ echo $#
6
$ echo ${!#}
f

Expansão de nomes de variáveis

  • ${!PREFIXO@}
  • ${!PREFIXO*}

Ambas expandem para os nomes das variáveis prefixadas por PREFIXO. A lista gerada será separada pelo primeiro caractere da variável $IFS.

Vamos listar os nomes das variáveis do sistema começadas com a cadeia GNOME (os nomes, não os seus valores):

$ echo ${!GNOME@}
GNOME_DESKTOP_SESSION_ID GNOME_KEYRING_PID GNOME_KEYRING_SOCKET
$ echo ${!GNOME*}
GNOME_DESKTOP_SESSION_ID GNOME_KEYRING_PID GNOME_KEYRING_SOCKET

Para finalizar, é necessário dizer que as Expansões de Parâmetros também se aplicam a vetores e aos parâmetros posicionais (aqueles que passamos para programas ou funções), sendo que nesses últimos, muitas vezes atuam com comportamento diferente do que mostramos.

Vou dar só um exemplo desse uso:

$ set {a..j}
$ echo $@
a b c d e f g h i j
$ echo ${@:0:3}
bash a b
$ echo ${@:1:3}
a b c


Veja a relação completa dos artigos de Colaboração: Julio Cezar N