Variáveis especiais: $?, $#, $@, $0 e outras

1. Introdução às Variáveis Especiais no Bash

No Bash, variáveis especiais são símbolos pré-definidos que fornecem informações sobre o ambiente de execução do shell, argumentos de scripts, códigos de saída e processos. Diferentemente das variáveis comuns que você define (como nome="João"), as variáveis especiais são sempre acessadas com o prefixo $ e têm significados fixos atribuídos pelo próprio shell.

Elas são essenciais para criar scripts robustos, pois permitem verificar o sucesso de comandos, processar argumentos de entrada e controlar o fluxo de execução. Essas variáveis funcionam tanto em scripts quanto em funções e na linha de comando interativa.

2. $? — Código de Saída do Último Comando

O $? armazena o código de retorno (exit status) do último comando executado. Por convenção, 0 indica sucesso, enquanto qualquer valor diferente de zero (1-255) indica erro.

$ ls /etc/passwd
/etc/passwd
$ echo $?
0

$ ls /arquivo_inexistente
ls: cannot access '/arquivo_inexistente': No such file or directory
$ echo $?
2

Uso prático em condicionais:

#!/bin/bash
grep "erro" /var/log/syslog
if [ $? -eq 0 ]; then
    echo "Erros encontrados no log"
else
    echo "Nenhum erro encontrado"
fi

Cuidado: $? é volátil — ele muda após cada comando. Sempre capture seu valor imediatamente:

ls /tmp
resultado=$?   # Salva antes de executar outro comando
echo "Comando ls retornou: $resultado"

3. $# — Número de Argumentos Passados

$# conta quantos argumentos foram passados para o script ou função. É fundamental para validação de entrada.

#!/bin/bash
# script: valida_args.sh
if [ $# -eq 0 ]; then
    echo "Erro: Nenhum argumento fornecido"
    echo "Uso: $0 <nome> <idade>"
    exit 1
fi

echo "Número de argumentos: $#"
echo "Primeiro argumento: $1"

Execução:

$ ./valida_args.sh
Erro: Nenhum argumento fornecido
Uso: ./valida_args.sh <nome> <idade>

$ ./valida_args.sh Maria 30
Número de argumentos: 2
Primeiro argumento: Maria

Combinado com shift para processar argumentos em lote:

#!/bin/bash
while [ $# -gt 0 ]; do
    echo "Processando: $1"
    shift
done

4. $@ e $* — Todos os Argumentos

Ambos representam todos os argumentos passados, mas com diferença crucial no tratamento de espaços.

$* trata todos os argumentos como uma única string. $@ preserva cada argumento individual. A diferença é mais evidente com aspas duplas:

#!/bin/bash
# script: test_args.sh
echo "Usando \$*:"
for arg in $*; do
    echo "  -> $arg"
done

echo "Usando \"\$@\":"
for arg in "$@"; do
    echo "  -> $arg"
done

Execução com argumentos contendo espaços:

$ ./test_args.sh "João Silva" "Maria Santos"
Usando $*:
  -> João
  -> Silva
  -> Maria
  -> Santos

Usando "$@":
  -> João Silva
  -> Maria Santos

Recomendação: Sempre use "$@" para preservar a integridade dos argumentos, especialmente ao passá-los para outros comandos:

#!/bin/bash
# Correto - preserva argumentos com espaços
grep "$@" arquivo.txt

# Incorreto - quebra argumentos com espaços
grep $* arquivo.txt

5. $0 — Nome do Script ou Shell

$0 contém o caminho do script em execução ou o nome do shell interativo.

#!/bin/bash
# script: info.sh
echo "Nome do script: $0"
echo "Diretório do script: $(dirname "$0")"
echo "Nome base: $(basename "$0")"

Execução:

$ ./info.sh
Nome do script: ./info.sh
Diretório do script: .
Nome base: info.sh

$ /home/user/scripts/info.sh
Nome do script: /home/user/scripts/info.sh
Diretório do script: /home/user/scripts
Nome base: info.sh

Diferença entre $0 e $BASH_SOURCE: Em scripts sourceados (com source ou .), $0 mantém o nome do script original, enquanto $BASH_SOURCE mostra o arquivo atual.

6. $$ e $! — IDs de Processo

$$ retorna o PID (Process ID) do shell atual. $! retorna o PID do último processo executado em segundo plano com &.

#!/bin/bash
echo "PID deste script: $$"

# Criar arquivo temporário único
tempfile="/tmp/meu_script_$$.tmp"
echo "Dados temporários" > "$tempfile"
echo "Arquivo criado: $tempfile"

# Executar processo em segundo plano
sleep 30 &
pid_fundo=$!
echo "Processo em segundo plano PID: $pid_fundo"

# Aguardar e verificar
wait $pid_fundo
echo "Processo $pid_fundo finalizado"

Aplicação prática: gerar nomes de arquivos temporários únicos para evitar conflitos entre múltiplas execuções do mesmo script.

7. Outras Variáveis Especiais Relevantes

$- — Flags ativas do shell:

$ echo $-
himBHs

Cada letra representa uma opção (ex.: h para hash, B para expansão de brace).

$_ — Último argumento do comando anterior:

$ mkdir -p /tmp/pasta_teste
$ cd $_
$ pwd
/tmp/pasta_teste

$LINENO — Número da linha atual no script, útil para debug:

#!/bin/bash
echo "Estou na linha $LINENO"
if [ -f "/etc/hosts" ]; then
    echo "Arquivo encontrado na linha $LINENO"
fi

$RANDOM — Gera número aleatório entre 0 e 32767:

#!/bin/bash
echo "Número aleatório: $RANDOM"
echo "Entre 1 e 10: $(( (RANDOM % 10) + 1 ))"

8. Boas Práticas e Armadilhas Comuns

Sempre usar aspas duplas em "$@", "$*" e "$0" para preservar espaços e caracteres especiais:

# Correto
cp "$@" /destino/

# Incorreto - quebra se argumentos tiverem espaços
cp $@ /destino/

Não confundir $? com saída de comando. $? é o código de retorno (número), não a saída textual. Para capturar saída, use $():

# Capturar saída (STDOUT)
saida=$(ls /tmp)

# Capturar código de retorno
ls /tmp
codigo=$?

Escopo: Variáveis especiais são locais ao shell/função atual. Dentro de uma função, $1, $#, $@ referem-se aos argumentos da função, não do script:

#!/bin/bash
minha_funcao() {
    echo "Args da função: $#"
}

minha_funcao "arg1" "arg2"
echo "Args do script: $#"

Dominar essas variáveis especiais é fundamental para escrever scripts Bash profissionais, confiáveis e portáveis. Elas fornecem o controle necessário para tratamento de erros, processamento de argumentos e gerenciamento de processos que todo shell script robusto exige.

Referências