test e [[ ]] - Condicionais em Shell Scripts
O Que Você Vai Aprender
- A diferença e os trade-offs entre
test,[ ]e[[ ]] - Como escolher os operadores corretos para strings, números e arquivos
- Como evitar estruturalmente bugs de quoting como
[: too many arguments
Resumo Rápido
- Na dúvida, use
[[ ]](o padrão seguro no bash) - Use
[ ](ou seja,test) apenas quando precisar de portabilidade POSIXsh - Quase todo bug vem de (1) aspas faltando ou (2) confusão de operadores (
-eqvs=)
Premissas
- Shell: bash (
[[ ]]é uma extensão do bash / ksh / zsh; não existe no POSIXsh) - Scripts começam com
#!/bin/bash
Qual a diferença entre test, [ ] e [[]]?
Conclusão:
[ ]é literalmente o comandotest.[[ ]]é uma palavra-chave do shell com quoting mais seguro, mais operadores e correspondência de padrões.
Aqui está a relação em uma tabela.
| Sintaxe | O que realmente é | Portabilidade | Principal força |
|---|---|---|---|
test EXPR |
Um builtin (também existe em disco) | POSIX (alta) | Mais fundamental |
[ EXPR ] |
Um alias para test (] é um arg) |
POSIX (alta) | Boa leitura com if |
[[ EXPR ]] |
Uma palavra-chave do shell | bash etc. (limitada) | Seguro p/ aspas, =~, &&/|| |
Como [ ] é um comando, você precisa de espaços ao redor de [, ] e cada operador. test e [ se comportam de forma idêntica.
$ test 1 -lt 2; echo $? 0 $ [ 1 -lt 2 ]; echo $? 0
[[ ]], por outro lado, é analisado como sintaxe do shell, o que o torna seguro para aspas e capaz de correspondência de padrões.
Confirme o que [ realmente é
$ type [ [ is a shell builtin $ ls -l /usr/bin/[ -rwxr-xr-x 1 root root ... /usr/bin/[
O builtin tem prioridade, mas test/[ também existem como comandos externos por razões históricas.
Como comparar strings?
Conclusão: Use
=para igualdade (==também funciona dentro de[[ ]]),-zpara vazio e-npara não vazio. Sempre coloque variáveis entre aspas duplas.
Operadores fundamentais
=... igual (==é sinônimo dentro de[[ ]])!=... diferente-z "$s"... string está vazia (comprimento zero)-n "$s"... string não está vazia
name="penguin"
if [[ "$name" = "penguin" ]]; then
echo "match"
fi
if [[ -z "$name" ]]; then
echo "empty"
else
echo "not empty"
fi= vs ==
== é uma extensão do bash. Para portabilidade POSIX sh, use = dentro de [ ]. Dentro de [[ ]] ambos funcionam.
[ "$a" = "$b" ] # Seguro para POSIX [[ "$a" == "$b" ]] # bash (tanto = quanto == funcionam dentro de [[ ]])
Como comparar números?
Conclusão: Use
-eq -ne -lt -le -gt -gepara números.=e>são operadores de string e significam algo diferente.
| Operador | Significado | Símbolo matemático |
|---|---|---|
-eq |
igual | == |
-ne |
diferente | != |
-lt |
menor que | < |
-le |
menor ou igual | <= |
-gt |
maior que | > |
-ge |
maior ou igual | >= |
count=42
if [[ "$count" -ge 10 ]]; then
echo "10 or more"
fiNão confunda = e -eq
[[ "08" = "8" ]] # false (diferentes como strings) [[ "08" -eq "8" ]] # true (iguais como numeros)
Usar = quando você quer uma comparação numérica leva a surpresas com zero-padding ou espaços em branco.
Se você quer aritmética, (( )) permite escrever símbolos matemáticos diretamente: (( count >= 10 )).
Como testar se um arquivo existe ou seu tipo?
Conclusão: Operadores de teste de arquivo verificam existência, tipo e permissões. Os mais comuns são
-e -f -d -r -w -x -s.
| Operador | Significado |
|---|---|
-e file |
existe (qualquer tipo) |
-f file |
é um arquivo regular |
-d file |
é um diretório |
-r file |
é legível |
-w file |
é gravável |
-x file |
é executável |
-s file |
tamanho maior que zero |
-L file |
é um link simbólico |
a -nt b |
a é mais novo que b |
config="/etc/myapp.conf"
if [[ -f "$config" && -r "$config" ]]; then
echo "readable config file exists"
else
echo "config missing or unreadable"
fiTestar "não existe"
Negue com !. Um padrão comum de retorno antecipado.
if [[ ! -d "$dir" ]]; then
echo "No such directory: $dir" >&2
exit 1
fiPor que [[]] é menos propenso a erros?
Conclusão: Dentro de
[[ ]], variáveis expandidas não sofrem word splitting ou globbing, então uma aspas faltando não quebra a sintaxe.
Como [ ] é um comando, uma variável vazia ou com espaço altera a contagem de argumentos e quebra a expressão.
v="" [ $v = "x" ] # vira [ = "x" ]: too many arguments / erro de sintaxe [ "$v" = "x" ] # vira [ "" = "x" ] (corretamente false) - aspas salvam v="a b" [ $v = "x" ] # vira [ a b = "x" ] (4 args, erro)
Dentro de [[ ]], uma variável expandida não é dividida nem globada, então mesmo uma aspas faltando raramente quebra algo.
v="a b" [[ $v = "x" ]] # nao quebra (false)
Mesmo assim, mantenha o hábito de usar aspas. Escrever "$var" tanto em [ ] quanto em [[ ]] mantém o código seguro independente de para onde você o porte.
O que são correspondência de padrões e regex do [[]]?
Conclusão: Dentro de
[[ ]], o lado direito de==é um padrão glob e=~é uma expressão regular. Nenhum dos dois existe em[ ].
Correspondência de padrão glob (==)
Deixe o lado direito sem aspas para um padrão; coloque aspas para corresponder como string literal.
file="report.txt"
if [[ "$file" == *.txt ]]; then
echo "text file"
fi
if [[ "$file" == "*.txt" ]]; then
echo "only matches the literal string *.txt"
fiCorrespondência regex (=~)
Deixe o lado direito sem aspas. Grupos capturados ficam no array BASH_REMATCH.
input="port=8080"
if [[ "$input" =~ ^port=([0-9]+)$ ]]; then
echo "port number: ${BASH_REMATCH[1]}"
fiSe você colocar aspas duplas no lado direito de =~, ele é tratado como string literal, não regex (bash 3.2+). A forma segura é colocar o padrão em uma variável: [[ "$s" =~ $re ]].
Como combinar múltiplas condições?
Conclusão: Dentro de
[[ ]]use&&e||. Com[ ], junte comandos separados:[ ... ] && [ ... ].
# Combinar dentro de [[ ]] (legivel)
if [[ "$age" -ge 18 && "$age" -lt 65 ]]; then
echo "working age"
fi
# Juntar comandos [ ] com &&
if [ "$age" -ge 18 ] && [ "$age" -lt 65 ]; then
echo "working age"
fiOs operadores -a (AND) e -o (OR) dentro de [ ] são depreciados. Sua análise de argumentos é ambígua e uma fonte frequente de bugs. Para combinar condições, divida em [ ] separados e junte com && / ||.
Resumo: quando usar test vs [[]]
Conclusão: Em scripts bash, use
[[ ]]por padrão e reserve[ ]para locais que precisam de POSIXsh. Acerte quoting e escolha de operador e os bugs praticamente desaparecem.
[[ ]]... apenas bash. Seguro para aspas, suporta=~,&&/||, e leitura fácil[ ]/test... quando precisar de#!/bin/shou compatibilidade POSIX- Strings usam
=/-z/-n, números usam a família-eq, arquivos usam-f/-d - Sempre coloque variáveis entre aspas como
"$var"
Template para copiar e colar
# Verificacao de argumento
if [[ $# -lt 1 ]]; then
echo "usage: $0 <file>" >&2
exit 1
fi
target="$1"
# Existencia e tipo de arquivo
if [[ ! -f "$target" ]]; then
echo "not a regular file: $target" >&2
exit 1
fi
# Ramificar por extensao
if [[ "$target" == *.log ]]; then
echo "processing a log file"
fi