test e [[ ]] - Condicionais em Shell Scripts
O Que Voce Vai Aprender
- A diferenca e os trade-offs entre
test,[ ]e[[ ]] - Como escolher os operadores corretos para strings, numeros e arquivos
- Como evitar estruturalmente bugs de quoting como
[: too many arguments
Resumo Rapido
- Na duvida, use
[[ ]](o padrao seguro no bash) - Use
[ ](ou seja,test) apenas quando precisar de portabilidade POSIXsh - Quase todo bug vem de (1) aspas faltando ou (2) confusao de operadores (
-eqvs=)
Premissas
- Shell: bash (
[[ ]]e uma extensao do bash / ksh / zsh; nao existe no POSIXsh) - Scripts comecam com
#!/bin/bash
Qual a diferenca entre test, [ ] e [[]]?
Conclusao:
[ ]e literalmente o comandotest.[[ ]]e uma palavra-chave do shell com quoting mais seguro, mais operadores e correspondencia de padroes.
Aqui esta a relacao em uma tabela.
| Sintaxe | O que realmente e | Portabilidade | Principal forca |
|---|---|---|---|
test EXPR |
Um builtin (tambem existe em disco) | POSIX (alta) | Mais fundamental |
[ EXPR ] |
Um alias para test (] e um arg) |
POSIX (alta) | Boa leitura com if |
[[ EXPR ]] |
Uma palavra-chave do shell | bash etc. (limitada) | Seguro p/ aspas, =~, &&/|| |
Como [ ] e um comando, voce precisa de espacos ao redor de [, ] e cada operador. test e [ se comportam de forma identica.
$ test 1 -lt 2; echo $? 0 $ [ 1 -lt 2 ]; echo $? 0
[[ ]], por outro lado, e analisado como sintaxe do shell, o que o torna seguro para aspas e capaz de correspondencia de padroes.
Confirme o que [ realmente e
$ type [ [ is a shell builtin $ ls -l /usr/bin/[ -rwxr-xr-x 1 root root ... /usr/bin/[
O builtin tem prioridade, mas test/[ tambem existem como comandos externos por razoes historicas.
Como comparar strings?
Conclusao: Use
=para igualdade (==tambem funciona dentro de[[ ]]),-zpara vazio e-npara nao vazio. Sempre coloque variaveis entre aspas duplas.
Operadores fundamentais
=... igual (==e sinonimo dentro de[[ ]])!=... diferente-z "$s"... string esta vazia (comprimento zero)-n "$s"... string nao esta vazia
name="penguin"
if [[ "$name" = "penguin" ]]; then
echo "match"
fi
if [[ -z "$name" ]]; then
echo "empty"
else
echo "not empty"
fi= vs ==
== e uma extensao 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 numeros?
Conclusao: Use
-eq -ne -lt -le -gt -gepara numeros.=e>sao operadores de string e significam algo diferente.
| Operador | Significado | Simbolo matematico |
|---|---|---|
-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"
fiNao confunda = e -eq
[[ "08" = "8" ]] # false (diferentes como strings) [[ "08" -eq "8" ]] # true (iguais como numeros)
Usar = quando voce quer uma comparacao numerica leva a surpresas com zero-padding ou espacos em branco.
Se voce quer aritmetica, (( )) permite escrever simbolos matematicos diretamente: (( count >= 10 )).
Como testar se um arquivo existe ou seu tipo?
Conclusao: Operadores de teste de arquivo verificam existencia, tipo e permissoes. Os mais comuns sao
-e -f -d -r -w -x -s.
| Operador | Significado |
|---|---|
-e file |
existe (qualquer tipo) |
-f file |
e um arquivo regular |
-d file |
e um diretorio |
-r file |
e legivel |
-w file |
e gravavel |
-x file |
e executavel |
-s file |
tamanho maior que zero |
-L file |
e um link simbolico |
a -nt b |
a e 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 "nao existe"
Negue com !. Um padrao comum de retorno antecipado.
if [[ ! -d "$dir" ]]; then
echo "No such directory: $dir" >&2
exit 1
fiPor que [[]] e menos propenso a erros?
Conclusao: Dentro de
[[ ]], variaveis expandidas nao sofrem word splitting ou globbing, entao uma aspas faltando nao quebra a sintaxe.
Como [ ] e um comando, uma variavel vazia ou com espaco altera a contagem de argumentos e quebra a expressao.
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 variavel expandida nao e dividida nem globada, entao mesmo uma aspas faltando raramente quebra algo.
v="a b" [[ $v = "x" ]] # nao quebra (false)
Mesmo assim, mantenha o habito de usar aspas. Escrever "$var" tanto em [ ] quanto em [[ ]] mantem o codigo seguro independente de para onde voce o porte.
O que sao correspondencia de padroes e regex do [[]]?
Conclusao: Dentro de
[[ ]], o lado direito de==e um padrao glob e=~e uma expressao regular. Nenhum dos dois existe em[ ].
Correspondencia de padrao glob (==)
Deixe o lado direito sem aspas para um padrao; 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"
fiCorrespondencia 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 voce colocar aspas duplas no lado direito de =~, ele e tratado como string literal, nao regex (bash 3.2+). A forma segura e colocar o padrao em uma variavel: [[ "$s" =~ $re ]].
Como combinar multiplas condicoes?
Conclusao: 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 [ ] sao depreciados. Sua analise de argumentos e ambigua e uma fonte frequente de bugs. Para combinar condicoes, divida em [ ] separados e junte com && / ||.
Resumo: quando usar test vs [[]]
Conclusao: Em scripts bash, use
[[ ]]por padrao 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 facil[ ]/test... quando precisar de#!/bin/shou compatibilidade POSIX- Strings usam
=/-z/-n, numeros usam a familia-eq, arquivos usam-f/-d - Sempre coloque variaveis 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