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: arg1 arg2 arg3
  • "$*" 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, getopts geralmente é uma escolha melhor para opções curtas. O tratamento de opções longas varia por plataforma, então teste cuidadosamente se usar getopt externo.

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.