Comparação de Condicionais Bash: Quando usar test, [ e [[
Compare test, colchetes simples e colchetes duplos para manter seus condicionais Bash portáteis, seguros e legíveis.
Comparação de Condicionais Bash: Quando usar test, [ e [[
Quando um condicional Bash se comporta de forma estranha, o problema geralmente é a construção que você escolheu. test, [ ] e [[ ]] parecem semelhantes, mas lidam com aspas, padrões, regex e portabilidade de forma diferente.
Este guia compara as três formas para que você possa escrever condicionais seguros, legíveis e adequados ao shell em que seu script realmente é executado.
O Comando test: A Fundação
O comando test é uma das formas mais antigas e fundamentais de avaliar condições em scripts de shell. É um comando embutido na maioria dos shells modernos e faz parte do padrão POSIX, tornando-o altamente portátil. test avalia uma expressão e retorna um status de saída de 0 (verdadeiro) ou 1 (falso).
Uso Básico
O comando test recebe um ou mais argumentos, que formam a expressão a ser avaliada. Ele verifica atributos de arquivo, comparações de strings e comparações de inteiros.
# Verifica se um arquivo existe
if test -f "meuarquivo.txt"; then
echo "meuarquivo.txt existe e é um arquivo regular."
fi
# Verifica se duas strings são iguais
NOME="Alice"
if test "$NOME" = "Alice"; then
echo "O nome é Alice."
fi
# Verifica se um número é maior que outro
CONTAGEM=10
if test "$CONTAGEM" -gt 5; then
echo "A contagem é maior que 5."
fi
Operadores Comuns do test
- Operadores de Arquivo:
-f(arquivo regular),-d(diretório),-e(existe),-s(não vazio),-r(legível),-w(gravável),-x(executável). - Operadores de String:
=(igual),!=(diferente),-z(string vazia),-n(string não vazia). - Operadores de Inteiro:
-eq(igual),-ne(diferente),-gt(maior que),-ge(maior ou igual),-lt(menor que),-le(menor ou igual).
Dica: Sempre coloque variáveis usadas com test entre aspas (ex.: "$NOME") para evitar problemas com divisão de palavras e expansão de nomes de caminho se o valor da variável contiver espaços ou caracteres glob.
Colchetes Simples [ ]: A Forma test
A construção de colchetes simples [ ] é uma sintaxe alternativa para o comando test. Em muitos shells, [ é um comando embutido, e os sistemas geralmente também fornecem um /usr/bin/[ externo. A principal diferença é que [ requer um ] de fechamento como seu último argumento. Assim como test, é compatível com POSIX.
Sintaxe e Semântica
# Equivalente a test -f "meuarquivo.txt"
if [ -f "meuarquivo.txt" ]; then
echo "meuarquivo.txt existe e é um arquivo regular usando [ ]."
fi
# Equivalente a test "$NOME" = "Alice"
NOME="Bob"
if [ "$NOME" != "Alice" ]; then
echo "O nome não é Alice."
fi
Observe o espaço obrigatório após [ e antes de ]. Eles são tratados como argumentos separados para o comando [.
Colocando Variáveis entre Aspas: Um Detalhe Crítico
Como [ ] é fundamentalmente o comando test, ele herda os mesmos comportamentos em relação à divisão de palavras e expansão de nomes de caminho. Isso significa que variáveis sem aspas podem levar a comportamentos inesperados ou vulnerabilidades de segurança.
Considere este exemplo:
#!/bin/bash
ENTRADA="arquivo com espaços.txt"
# PERIGOSO: Variável sem aspas causará problemas se ENTRADA contiver espaços
# O shell realizará a divisão de palavras, tratando "arquivo" e "com espaços.txt" como argumentos separados
# levando a um erro de sintaxe ou avaliação incorreta.
# if [ -f $ENTRADA ]; then echo "Encontrado"; else echo "Não encontrado"; fi
# CORRETO: Coloque a variável entre aspas para tratá-la como um único argumento
if [ -f "$ENTRADA" ]; then
echo "'arquivo com espaços.txt' existe."
else
echo "'arquivo com espaços.txt' não existe ou não é um arquivo regular."
fi
Sem aspas, $ENTRADA se expandiria para arquivo com espaços.txt, e [ -f arquivo com espaços.txt ] seria interpretado como um erro de sintaxe pelo comando [ porque -f espera apenas um operando. Colocar entre aspas garante que $ENTRADA seja passado como um único argumento, "arquivo com espaços.txt".
Perigos da Divisão de Palavras e Expansão de Nomes de Caminho
Tanto test quanto [ estão sujeitos aos comportamentos padrão do shell de divisão de palavras e expansão de nomes de caminho (globbing). Se uma variável contiver espaços ou caracteres glob (*, ?, [ ]) e não estiver entre aspas, o shell a expandirá antes que test ou [ vejam os argumentos. Isso pode levar a erros de sintaxe ou comparações incorretas quando caracteres glob corresponderem a arquivos existentes.
Colchetes Duplos [[ ]]: A Palavra-chave Moderna do Bash
A construção de colchetes duplos [[ ]] é uma palavra-chave do Bash (também suportada por Ksh e Zsh), não um comando externo ou um alias. Essa distinção é crucial, pois permite que [[ ]] se comporte de forma diferente e ofereça funcionalidades aprimoradas e segurança melhorada em comparação com test ou [ ].
Funcionalidades Aprimoradas
[[ ]] introduz vários recursos poderosos não disponíveis com test ou [:
Sem Divisão de Palavras ou Expansão de Nomes de Caminho: Variáveis dentro de
[[ ]]geralmente não precisam ser colocadas entre aspas (embora seja uma boa prática fazê-lo para maior clareza). O shell lida com o conteúdo de[[ ]]como uma única unidade, evitando a divisão de palavras e a expansão de nomes de caminho. Isso reduz significativamente erros comuns de script e riscos de segurança.# Não é necessário colocar variáveis entre aspas (embora ainda seja seguro fazê-lo) ENTRADA="arquivo com espaços.txt" if [[ -f $ENTRADA ]]; then # $ENTRADA é tratado como uma única string aqui echo "'$ENTRADA' existe." fiGlobbing para Comparação de Strings: Os operadores
==e!=realizam correspondência de padrões (globbing) em vez de igualdade estrita de strings quando usados dentro de[[ ]]. Isso significa que você pode usar*,?e[]como curingas.NOME_ARQUIVO="meu_documento.txt" if [[ "$NOME_ARQUIVO" == *".txt" ]]; then # Verifica se NOME_ARQUIVO termina com .txt echo "É um arquivo de texto!" fi # Nota: Para igualdade estrita de strings sem globbing, use `test` ou `[ ]` com `=` # ou certifique-se de que nenhum caractere glob esteja presente no lado direito de `==` em [[ ]] # (ou coloque o lado direito entre aspas se ele contiver caracteres glob literais que você deseja corresponder literalmente).Correspondência de Expressões Regulares: O operador
=~permite realizar correspondência de expressões regulares.
ENDERECO_IP="192.168.1.100"
if [[ "$ENDERECO_IP" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
echo "Formato de IP válido."
fi
# Importante: O padrão regex no lado direito de =~ geralmente NÃO deve ser colocado entre aspas
# se contiver caracteres que seriam tratados como padrões glob.
# Se o regex estiver em uma variável, ele também deve estar sem aspas.
# Exemplo de padrão: ^[A-Za-z]+$
```
4. **Operadores Lógicos `&&` e `||`**: `[[ ]]` suporta os operadores lógicos mais intuitivos no estilo C `&&` (E) e `||` (OU) para combinar múltiplas condições, juntamente com `!` para negação. Esses operadores têm avaliação de curto-circuito e precedência adequadas, ao contrário de `-a` e `-o` do `test`.
```bash
IDADE=25
if [[ "$NOME" == "Alice" && "$IDADE" -ge 18 ]]; then
echo "Alice é adulta."
fi
if [[ "$USUARIO" == "root" || -w /etc/fstab ]]; then
echo "Ou é root ou pode escrever em fstab."
fi
```
### Natureza Específica do Bash
Embora `[[ ]]` ofereça vantagens significativas, sua principal desvantagem é que é uma extensão do Bash/Ksh/Zsh e não faz parte do padrão POSIX. Isso significa que scripts que dependem de `[[ ]]` podem não ser portáveis para `sh`, `dash` ou sistemas Unix-like mais antigos/mínimos.
## Comparação Lado a Lado: `test` vs. `[` vs. `[[`
Aqui está uma tabela resumindo as principais diferenças:
| Característica | `test` | `[ ]` | `[[ ]]` |
| :------------------------- | :------------------------------- | :---------------------------------- | :---------------------------------------- |
| **Tipo** | Comando embutido (ou externo) | Comando embutido (alias para `test`) | Palavra-chave do shell (Bash, Ksh, Zsh) |
| **Compatível com POSIX** | Sim | Sim | Não |
| **`]` de Fechamento Obrigatório** | Não | Sim (como último argumento) | Sim (como parte da palavra-chave) |
| **Divisão de Palavras** | Sim, em variáveis sem aspas | Sim, em variáveis sem aspas | Não, variáveis são tratadas como strings únicas |
| **Expansão de Nomes de Caminho** | Sim, em variáveis sem aspas | Sim, em variáveis sem aspas | Não |
| **Correspondência de Padrões Glob** | Não para igualdade de strings | Não para igualdade de strings | Sim com lado direito sem aspas de `==` ou `!=` |
| **Expressões Regulares** | Não | Não | Sim com `=~` |
| **E/OU Lógicos** | `-a`, `-o` existem, mas são fáceis de interpretar erroneamente | `-a`, `-o` existem, mas são fáceis de interpretar erroneamente | `&&`, `||` com comportamento normal de curto-circuito |
| **Comandos Compostos** | Requer chamadas `test` separadas | Requer chamadas `[` separadas | Pode combinar expressões diretamente (`&&`/`||`)|
| **Colocação de Variáveis entre Aspas** | **Obrigatória** para segurança | **Obrigatória** para segurança | Geralmente não é necessária, mas é uma boa prática |
## Quando Usar Cada Um
Escolher a construção condicional correta depende principalmente dos seus requisitos de portabilidade e da complexidade da sua lógica condicional.
### Conformidade POSIX vs. Recursos Modernos do Bash
- **Use `test` ou `[ ]` quando...**
- **A portabilidade é fundamental**: Se o seu script precisa ser executado em qualquer shell compatível com POSIX (`sh`, `dash`, sistemas mais antigos, etc.), `test` ou `[ ]` são suas únicas opções confiáveis.
- Suas condições são simples (verificações de arquivo, comparações básicas de strings/inteiros).
- Você se sente confortável com a colocação cuidadosa de todas as variáveis entre aspas e usando `&&`/`||` no nível do shell fora dos colchetes quando precisar de lógica composta.
- **Use `[[ ]]` quando...**
- **Você está escrevendo exclusivamente para Bash** (ou Ksh/Zsh) e não precisa de portabilidade POSIX.
- Você precisa de recursos avançados como correspondência de padrões glob, correspondência de expressões regulares ou operadores lógicos `&&`/`||` no estilo C.
- Você deseja os recursos de segurança aprimorados que evitam a divisão de palavras e a expansão de nomes de caminho, levando a um código mais robusto e menos propenso a erros.
- Suas condições envolvem lógica complexa que seria complicada com `test -a`/`-o`.
### Melhores Práticas e Recomendações
1. **Priorize `[[ ]]` para Scripts Bash**: Se o seu script é destinado ao Bash, `[[ ]]` é geralmente a escolha preferida devido à sua segurança aumentada, funcionalidade estendida e sintaxe mais intuitiva para condições complexas. Ele reduz drasticamente erros comuns de script relacionados a aspas e caracteres especiais.
2. **Sempre Coloque Aspas em `test` e `[ ]`**: Se você *precisar* usar `test` ou `[ ]` para conformidade POSIX, crie o hábito de **sempre colocar suas variáveis entre aspas** para evitar comportamentos inesperados devido à divisão de palavras e expansão de nomes de caminho.
```bash
# Boa prática para [ ] e test
VAR="uma string com espaços"
if [ -n "$VAR" ]; then echo "Não vazio"; fi
```
3. **Esteja Atento à Correspondência de Padrões**: Em `test` e `[ ]`, `=` é usado para igualdade de strings. Em `[[ ]]`, `=` e `==` podem realizar correspondência de padrões quando o lado direito não está entre aspas. Coloque o lado direito entre aspas quando quiser uma comparação literal de strings.
4. **Expressões Regulares com `=~`**: Ao usar `=~` em `[[ ]]`, o lado direito normalmente deve estar sem aspas para permitir que o shell o interprete como um padrão de expressão regular, não como uma string literal para corresponder.
```bash
# Padrão regex sem aspas está correto para =~ em [[ ]]
if [[ "$LINHA" =~ ^Erro: ]]; then echo "Erro encontrado"; fi
```
## Conclusão
Use `[ ]` ou `test` quando seu script precisar ser executado sob POSIX `sh`. Use `[[ ]]` quando seu shebang for Bash e você quiser manipulação de variáveis mais segura, correspondência glob, correspondência regex e condições compostas mais limpas. O hábito principal é simples: combine a sintaxe condicional com o shell e coloque aspas deliberadamente.