Dominando Parâmetros Posicionais: Um Guia para Argumentos de Scripts Bash
Desbloqueie o poder de scripts Bash dinâmicos dominando parâmetros posicionais. Este guia abrangente explica como acessar argumentos de linha de comando usando `$1`, `$2` e variáveis especiais como `$#` (contagem de argumentos) e o crucial `"$@"` (todos os argumentos). Aprenda as melhores práticas essenciais para validação de entrada, entenda a diferença entre `\$*` e `\$@`, e veja exemplos práticos para escrever scripts robustos e com verificação de erros que se adaptam perfeitamente à entrada do usuário.
Dominando Parâmetros Posicionais: Um Guia para Argumentos de Scripts Bash
Os scripts Bash se tornam muito mais úteis quando aceitam argumentos, em vez de forçá-lo a editar variáveis dentro do arquivo. Um script de backup deve aceitar um diretório de origem. Um script de implantação deve aceitar um nome de ambiente. Um script de limpeza deve aceitar um ou mais caminhos. Esses valores chegam como parâmetros posicionais: $1, $2, $3 e assim por diante.
A parte complicada não é ler $1. A parte complicada é lidar com argumentos ausentes, argumentos com espaços, flags opcionais e o momento em que seu script cresce de "só para mim" para algo que outra pessoa executará às 2 da manhã.
A Anatomia dos Parâmetros Posicionais
Parâmetros posicionais são variáveis especiais definidas pelo shell que correspondem às palavras fornecidas na linha de comando após o nome do script. Eles são numerados sequencialmente, começando em 1.
| Parâmetro | Descrição | Valor de Exemplo (ao executar ./script.sh arquivo1 dir/) |
|---|---|---|
$0 |
O nome do script em si (ou função). | ./script.sh |
$1 |
O primeiro argumento passado para o script. | arquivo1 |
$2 |
O segundo argumento passado para o script. | dir/ |
$N |
O enésimo argumento (onde N > 0). | |
${10} |
Argumentos além de 9 devem ser colocados entre chaves. |
Acessando Argumentos Além de $9
Enquanto os argumentos 1 a 9 são acessados diretamente como $1 a $9, acessar o décimo argumento e argumentos subsequentes requer colocar o número entre chaves para evitar ambiguidade com variáveis de ambiente ou operações de string (por exemplo, ${10} em vez de $10).
Parâmetros Especiais Essenciais para Scripts
Além dos parâmetros numéricos, o Bash fornece várias variáveis especiais críticas que se relacionam com o conjunto de argumentos como um todo. Estas são indispensáveis para validação e iteração.
Contando Argumentos com $#
A variável especial $# contém o número total de argumentos de linha de comando passados para o script (excluindo $0). Esta é talvez a variável mais importante para implementar validação de entrada.
#!/bin/bash
if [ "$#" -eq 0 ]; then
echo "Erro: Nenhum argumento fornecido."
echo "Uso: $0 <arquivo_de_entrada>"
exit 1
fi
echo "Você forneceu $# argumentos."
Todos os Argumentos: $@ e $*
As variáveis $@ e $* ambas representam a lista completa de argumentos, mas se comportam de forma diferente—especialmente quando entre aspas.
$* (String Única)
Quando entre aspas duplas ("$*"), a lista inteira de parâmetros posicionais é tratada como um único argumento, separado pelo primeiro caractere da variável IFS (Internal Field Separator) (geralmente um espaço).
- Se os argumentos de entrada forem:
arg1arg2arg3 "$*"expande para:"arg1 arg2 arg3"(um único elemento)
$@ (Strings Separadas - Preferido)
Quando entre aspas duplas ("$@"), cada parâmetro posicional é tratado como um argumento separado e entre aspas. Este é o método padrão e preferido para iterar sobre argumentos, pois preserva corretamente argumentos contendo espaços.
- Se os argumentos de entrada forem:
arg1"arg with space"arg3 "$@"expande para:"arg1" "arg with space" "arg3"(três elementos distintos)
Por Que Aspas Importam: Uma Demonstração
Considere um script executado com argumentos: ./test.sh 'hello world' arquivo.txt
#!/bin/bash
# $* sem aspas divide em espaços e geralmente está errado.
echo "-- Loop usando $* sem aspas --"
for item in $*; do
echo "Item: $item"
done
# "$@" com aspas preserva cada argumento original.
echo "-- Loop usando "$@" com aspas --"
for item in "$@"; do
echo "Item: $item"
done
Com ./test.sh 'hello world' arquivo.txt, o loop sem aspas imprime hello e world como itens separados. O loop "$@" mantém hello world como um argumento. Essa diferença é a razão pela qual usuários experientes de shell recorrem a "$@" quase automaticamente.
Técnicas Práticas para Manipulação de Argumentos
1. Script Básico de Recuperação de Argumentos
Este script simples demonstra como acessar parâmetros específicos e usar $0 para fornecer feedback útil.
deploy_service.sh:
#!/bin/bash
# Uso: deploy_service.sh <nome_do_servico> <ambiente>
NOME_SERVICO="$1"
AMBIENTE="$2"
# Verificação de validação (mínimo de dois argumentos)
if [ "$#" -lt 2 ]; then
echo "Uso: $0 <nome_do_servico> <ambiente>"
exit 1
fi
echo "Iniciando implantação para o serviço: $NOME_SERVICO"
echo "Ambiente alvo: $AMBIENTE"
# Executar comando usando os parâmetros validados
ssh admin@servidor-"$AMBIENTE" "/caminho/para/iniciar $NOME_SERVICO"
2. Validação Robusta de Entrada
Bons scripts sempre validam a entrada antes de prosseguir. Isso inclui verificar a contagem ($#) e frequentemente verificar o conteúdo dos argumentos (por exemplo, verificar se um argumento é um número ou um caminho de arquivo válido).
#!/bin/bash
# 1. Verificar Contagem de Argumentos (Deve ser exatamente 3)
if [ "$#" -ne 3 ]; then
echo "Erro: Este script requer três argumentos (origem, destino, usuário)."
echo "Uso: $0 <caminho_origem> <caminho_destino> <usuário>"
exit 1
fi
CAMINHO_ORIGEM="$1"
CAMINHO_DESTINO="$2"
USUARIO="$3"
# 2. Verificar Conteúdo (Exemplo: Verificar se o caminho de origem existe)
if [ ! -f "$CAMINHO_ORIGEM" ]; then
echo "Erro: Arquivo de origem '$CAMINHO_ORIGEM' não encontrado ou não é um arquivo."
exit 2
fi
# Se a validação passar, prosseguir
echo "Copiando $CAMINHO_ORIGEM para $CAMINHO_DESTINO como usuário $USUARIO..."
Dica de Melhor Prática: Sempre forneça uma declaração
Uso:clara e concisa quando a validação falhar. Isso ajuda os usuários a corrigir rapidamente a invocação do comando.
3. Iterando Argumentos com shift
O comando shift é uma excelente ferramenta para processar argumentos sequencialmente, frequentemente usado ao lidar com flags simples ou ao processar argumentos um por um dentro de um loop while.
shift descarta o argumento $1 atual, move $2 para $1, $3 para $2, e decrementa $# em um. Isso permite processar o primeiro argumento e então fazer o loop até que nenhum argumento reste.
#!/bin/bash
# Processar uma flag -v simples e então listar os arquivos restantes
VERBOSO=false
if [ "$1" = "-v" ]; then
VERBOSO=true
shift # Descartar a flag -v e deslocar os argumentos para cima
fi
if $VERBOSO; then
echo "Modo verboso ativado."
fi
if [ "$#" -eq 0 ]; then
echo "Nenhum arquivo especificado."
exit 0
fi
echo "Processando $# arquivos restantes:"
for arquivo in "$@"; do
if $VERBOSO; then
echo "Verificando arquivo: $arquivo"
fi
# ... lógica de processamento aqui
done
Nota:
shifté útil para análise simples. Para scripts complexos com muitas flags,getoptsgeralmente é uma escolha melhor para opções curtas. O tratamento de opções longas varia por plataforma, então teste cuidadosamente se usargetoptexterno.
Um Analisador Mais Realista
Muitos scripts internos começam com uma flag opcional e um valor obrigatório. Aqui está um pequeno padrão que permanece legível:
#!/usr/bin/env bash
set -u
execucao_seca=false
ambiente=""
uso() {
echo "Uso: $0 [--dry-run] --env <dev|staging|prod> <arquivo>..." >&2
}
while [ "$#" -gt 0 ]; do
case "$1" in
--dry-run)
execucao_seca=true
shift
;;
--env)
if [ "$#" -lt 2 ]; then
echo "Erro: --env requer um valor." >&2
uso
exit 2
fi
ambiente="$2"
shift 2
;;
--help|-h)
uso
exit 0
;;
--)
shift
break
;;
-*)
echo "Erro: opção desconhecida: $1" >&2
uso
exit 2
;;
*)
break
;;
esac
done
if [ -z "$ambiente" ]; then
echo "Erro: --env é obrigatório." >&2
uso
exit 2
fi
if [ "$#" -eq 0 ]; then
echo "Erro: forneça pelo menos um arquivo." >&2
uso
exit 2
fi
for arquivo in "$@"; do
if [ ! -f "$arquivo" ]; then
echo "Erro: arquivo não encontrado: $arquivo" >&2
exit 3
fi
if $execucao_seca; then
echo "Enviaria $arquivo para $ambiente"
else
echo "Enviando $arquivo para $ambiente"
# comando de upload vai aqui
fi
done
Observe os detalhes tediosos. Mensagens de erro vão para stderr. -- significa "parar de analisar opções", o que permite que alguém passe um nome de arquivo que comece com um traço. O loop final de arquivos usa "$@", então notas de versão.txt permanece um nome de arquivo.
Erros Comuns
O erro mais comum é esquecer as aspas:
cp $1 $2
Isso quebra quando qualquer caminho contém espaços ou caracteres glob do shell. Use:
cp -- "$1" "$2"
O -- diz a muitos comandos que a análise de opções terminou, o que ajuda se um caminho começar com -.
Outro erro comum é validar tarde demais. Se seu script espera dois argumentos, verifique isso antes de fazer qualquer coisa destrutiva:
if [ "$#" -ne 2 ]; then
echo "Uso: $0 <origem> <destino>" >&2
exit 2
fi
Use códigos de saída distintos quando ajudar o chamador. Um erro de uso pode ser 2; um arquivo ausente pode ser 3; um comando externo com falha pode manter seu próprio status. Você não precisa de uma taxonomia gigante de códigos de saída, mas retornar 0 após uma invocação ruim torna a automação menos confiável.
Funções Também Têm Parâmetros Posicionais
Dentro de uma função Bash, $1 e $2 referem-se aos argumentos da função, não aos argumentos originais do script.
log_copia() {
local origem="$1"
local destino="$2"
echo "Copiando $origem para $destino"
cp -- "$origem" "$destino"
}
log_copia "$1" "$2"
Isso é útil, mas pode surpreendê-lo se você esperava que $1 dentro da função significasse o primeiro argumento do script. Passe valores explicitamente. Isso torna a função mais fácil de testar e mais fácil de reutilizar.
Encaminhando Argumentos para Outro Comando
Muitos scripts wrapper existem apenas para adicionar uma pequena configuração antes de chamar outro comando. Nesse caso, "$@" é o que mantém o wrapper honesto.
#!/usr/bin/env bash
set -e
export APP_ENV=staging
exec /usr/local/bin/meuapp "$@"
Se alguém executar:
./executar-staging.sh --config "config com espacos.yml" --verbose
O comando encapsulado recebe os mesmos três argumentos. Se você usasse $* ou $@ sem aspas, o caminho da configuração poderia ser dividido em várias palavras.
exec é opcional, mas geralmente é útil em wrappers porque substitui o processo do shell pelo processo alvo. Isso faz com que os sinais se comportem de forma mais previsível sob systemd, Docker ou um supervisor de processos.
Padrões Sem Surpresas
Às vezes, um argumento deve ser opcional. A expansão de parâmetros do Bash pode ajudar:
ambiente="${1:-dev}"
Isso significa "use $1 se estiver definido e não vazio; caso contrário, use dev." Isso é bom para scripts locais amigáveis, mas tenha cuidado com scripts de produção. Um padrão silencioso pode implantar no ambiente errado se alguém esquecer um argumento.
Para comandos arriscados, prefira entrada explícita:
if [ "$#" -lt 1 ]; then
echo "Uso: $0 <ambiente>" >&2
exit 2
fi
Padrões são melhores quando a consequência é pequena, como definir um nível de log ou diretório de saída. Eles são arriscados quando o argumento escolhe um servidor, exclui dados ou altera um destino de implantação.
Parâmetros Posicionais e set -u
Muitos scripts Bash usam set -u para que variáveis não definidas causem um erro. Isso pode pegar erros de digitação, mas também muda como parâmetros posicionais ausentes se comportam.
#!/usr/bin/env bash
set -u
echo "Primeiro argumento: $1"
Execute esse script sem argumentos e o Bash sai com um erro de "variável não vinculada". Esse erro é tecnicamente correto, mas não é amigável. Valide $# antes de ler parâmetros obrigatórios:
if [ "$#" -lt 1 ]; then
echo "Uso: $0 <arquivo-de-entrada>" >&2
exit 2
fi
arquivo_entrada="$1"
Para parâmetros opcionais sob set -u, use uma expansão protegida:
modo="${2:-padrao}"
Isso mantém o modo estrito útil sem fazer com que valores opcionais ausentes quebrem o script.
Quando Parâmetros Posicionais São a Interface Errada
Parâmetros posicionais são ótimos para comandos pequenos:
backup.sh /var/www /backup/www.tar.gz
Eles se tornam difíceis de ler quando o script aceita muitos valores:
deploy.sh prod us-east-1 api v2.4.1 true false 30
Ninguém quer lembrar o que o quinto argumento significa. Quando um script atinge esse ponto, use flags nomeadas ou um arquivo de configuração:
deploy.sh --env prod --region us-east-1 --service api --version v2.4.1 --timeout 30
O código é um pouco mais longo, mas a linha de comando se torna autodocumentada. Essa é uma boa troca para scripts usados por uma equipe.
O bom tratamento de parâmetros posicionais é principalmente disciplina: valide cedo, coloque aspas em toda expansão a menos que você queira intencionalmente dividir, use "$@" para encaminhar argumentos e mantenha mensagens de uso próximas às verificações que as acionam. Esses hábitos fazem com que scripts pequenos sobrevivam a nomes de arquivos reais, usuários reais e automação real.