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
Data de Publicação: 12 de abril de 2020
[
(O comando test
) e [[
(normalmente chamado de comando novo
test
) são usados para avaliar condições. [[
funciona somente no Bash, Korn
Shell e zsh; [
e test
estão implementados em qualquer Shell compatível com
o padrão POSIX. Apesar de todos os Shells modernos terem implementações
internas (builtin) de [
, geralmente este ainda é um executável externo,
sendo normalmente /bin/[
ou /usr/bin/[
. Veja isso:
$ which [ /usr/bin/[ $ which [[ $ whereis -b [ [: /usr/bin/[ $ whereis -b [[ [[:
A opção -b
do comando whereis
, serve para que ele mostre somente
onde está o binário (código propriamente dito) do arquivo e, pelo resultado
das linhas acima, podemos ver que não existe código externo do [[
, isto é,
ele é um intrínseco (builtin) e por isso podemos concluir que é mais veloz.
Embora [
e [[
tenham muito em comum, e compartilhem muitos operadores
como -f
, -s
, -n
, -z
, há algumas diferenças notáveis, sendo
as duas mais importantes é que o novo test
([[
) permite:
Esse novo tipo de construção é legal porque permite usar metacaracteres de expansão de arquivos para comparação. Esses coringas atendem às normas de Geração de Nome de Arquivos (File Name Generation).
Exemplo
$ echo $H 13 $ [[ $H == [0-9] || $H == 1[0-2] ]] || echo Hora inválida Hora inválida
Nesse exemplo, testamos se o conteúdo da variável $H
estava compreendido
entre zero e nove ([0-9]
) ou (||
) se estava entre dez e doze
(1[0-2]
), dando uma mensagem de erro caso não estivesse.
Como você pode imaginar, esse uso de padrões para comparação aumenta muito
o poderio do comando test
.
Outro grande trunfo deste comando é que ele suporta expressões regulares para especificar condições usando a seguinte sintaxe:
[[ CAD =~ REGEX ]]
Onde REGEX é uma expressão regular (que pode inclusive usar metacaracteres das Expressões Regulares avançadas). Assim sendo, poderíamos montar uma rotina para crítica de horários com a seguinte construção:
if [[ $Hora =~ ([01][0-9]|2[0-3]):[0-5][0-9] ]] then echo Horario OK else echo O horario informado esta incorreto fi
DICA: As subcadeias que casam com expressões entre parênteses são salvas no vetor
BASH_REMATCH
. O elemento de BASH_REMATCH
com índice 0 é a porção da cadeia que
casou com a expressão regular inteira. O elemento de BASH_REMATCH
com índice
n
é a porção da cadeia que casou com a enésima expressão entre parênteses.
Vamos executar direto no prompt o comando anterior para entender melhor:
$ Hora=12:34 $ if [[ $Hora =~ ([01][0-9]|2[0-3]):[0-5][0-9] ]] > then > echo Horario OK > else > echo O horario informado esta incorreto > fi Horario OK $ echo ${BASH_REMATCH[@]} # Lista todos elementos do vetor 12:34 12 $ echo ${BASH_REMATCH[0]} # Índice zero tem o casamento total 12:34 $ echo ${BASH_REMATCH[1]} # Índice 1 contem o retrovisor 1. Repare o grupo 12
No primeiro echo que fizemos, o caractere arroba (@
) representa todos
os elementos do vetor, em seguida, vimos que o elemento índice zero [0]
está com a hora inteira e o elemento [1] está com a subcadeia que casou com
[01][0-9]|2[0-3]
, isto é, a expressão que estava entre parênteses. Em outras
palavras o índice N
(com N
maior que zero) atua como se fosse o retrovisor
\N
, com uma ressalva importante: só podemos criar até 9 retrovisores, mas
nesse vetor, tal limite não existe.
Vamos ver se com outro exemplo pode ficar mais claro.
$ if [[ supermercado =~ (mini|(su|hi)per)?mercado ]]
> then
> echo Todos os elementos - ${BASH_REMATCH[@]} # O mesmo que echo ${BASH_REMATCH[*]}
> echo Vetor completo - ${BASH_REMATCH[0]} # O mesmo que echo $BASH_REMATCH
> echo Elemento indice 1 - ${BASH_REMATCH[1]}
> echo Elemento indice 2 - ${BASH_REMATCH[2]}
> fi
Todos os elementos - supermercado super su
Vetor completo - supermercado
Elemento indice 1 - super
Elemento indice 2 - su
Agora que vocês entenderam o uso dessa variável, vou mostrar uma de suas
grandes utilizações, já que aposto como não perceberam que enrolei vocês
desde o primeiro exemplo desta seção. Para isso, vamos voltar ao exemplo da
crítica de horas, porém mudando o valor da variável $Hora
. Veja só como as
Expressões Regulares podem nos enganar:
$ Hora=54321:012345
$ if [[ $Hora =~ ([01][0-9]|2[0-3]):[0-5][0-9] ]]
> then
> echo Horario OK
> else
> echo O horario informado esta incorreto
> fi
Horario OK
Epa! Isso era para dar errado! Vamos ver o que aconteceu:
$ echo ${BASH_REMATCH[0]}
21:01
Ihhh, casou somente com os dois caracteres que estão antes e os dois que
estão depois dos dois pontos (:
). Viu como eu disse que tinha enrolado você?
Para que isso ficasse perfeito faltou colocar as âncoras das Expressões
Regulares, isto é, um circunflexo (^
) para marcar o início e um cifrão
($
) para marcar o final. Veja:
$ Hora=54321:012345
$ if [[ $Hora =~ ^([01][0-9]|2[0-3]):[0-5][0-9]$ ]]
> then
> echo Horario OK
> else
> echo O horario informado esta incorreto
> fi
O horario informado esta incorreto
-a
e -o
, e o agrupador (...)
, são definidos pelo padrão
POSIX, mas apenas para casos estritamente limitados, pois são marcados como
obsoletos. O uso desses operadores é desencorajado e é melhor você usar
vários comandos [
.
Prefira fazer:
if [ "$a" = a ] && [ "$b" = b ]; ... if [ "$a" = a ] || { [ "$b" = b ] && [ "$c" = c ];}; ...
no lugar de:
if [ "$a" = a -a "$b" = b ]; then ... if [ "$a" = a ] -o \( [ "$b" = b ] -a [ "$c" = c ] \); ...
$ ls arq* arq arq1 arq2 arqr $ [ -f arq* ] || echo Não existe # Quando expandir dá erro bash: [: número excessivo de argumentos Não existe $ [[ -f arq* ]] || echo Não existe Não existe # Não expandiu e não achou arq* $ var="a b" $ [ -z $var ] || echo Tem dado # Após a expansão virará 2 argumentos bash: [: a: esperado operador binário Tem dado $ [[ -z $var ]] || echo Tem dado # Perfeito! Tem dado $ > "Nome Ruim" # Criei arquivo Nome Ruim $ Arq="Nome Ruim" $ [ -f $Arq ] && echo "$Arq é um arquivo" bash: [: Arq: esperado operador binário $ [[ -f $Arq ]] && echo "$Arq é um arquivo" Nome Ruim é um arquivo
Como você pode ver, o fato de você não precisar usar aspas nem apóstrofos
torna o uso de [[
mais fácil e menos propenso a erros do que o [
.
2. Usando [[
, os parênteses não precisam ser "escapados":
$ [[ -f $Arq1 && ( -d $dir1 || -d $dir2) ]] $ [ -f "$Arq1" -a \( -d "$dir1" -o -d "$dir2" \) ]
3. A partir do Bash 4.1, a comparação de cadeias usando maior que (>
) e menor
que (<
) respeita as definições correntes do comando locale
quando feita com
[[
. As versões anteriores do Bash não respeitavam o locale
. O [
e o test
também não o respeitam em nenhuma versão (embora as man pages digam que sim).
Como regra, aconselho sempre usar [[
para testar condições que envolvam
cadeias e arquivos. Se você quiser comparar números, use uma expressão
aritmética (( ... ))
.
Quando deve ser usado o novo comando test ([[
) e quando se deve usar o
antigo ([
)? Se a portabilidade POSIX ou o Bourne Shell for uma preocupação,
a sintaxe antiga deve ser usada. Se, por outro lado, o script requer bash,
zsh ou korn shell, a nova sintaxe é muito mais flexível.
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