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.