Argumentos em scripts: $1, $2 e shift

1. Fundamentos dos Argumentos Posicionais

Quando você executa um script Bash, pode passar informações adicionais na linha de comando. Essas informações são chamadas de argumentos posicionais e são acessadas através das variáveis especiais $1, $2, $3 e assim por diante. O $1 representa o primeiro argumento, $2 o segundo, e assim sucessivamente até $N.

#!/bin/bash
# script: mostrar_args.sh
echo "Nome do script: $0"
echo "Primeiro argumento: $1"
echo "Segundo argumento: $2"
echo "Terceiro argumento: $3"

Execute com: ./mostrar_args.sh alfa beta gama

Nome do script: ./mostrar_args.sh
Primeiro argumento: alfa
Segundo argumento: beta
Terceiro argumento: gama

A variável $0 é especial: ela contém o nome do script (ou do shell) em execução. Diferente das variáveis de ambiente, que existem no contexto do shell como um todo, os argumentos posicionais são locais ao script e dependem exclusivamente do que foi digitado na linha de comando no momento da execução.

2. Navegando pelos Argumentos com $# e $@

Para trabalhar eficientemente com argumentos, o Bash oferece duas variáveis especiais adicionais:

  • $#: contém o número total de argumentos passados (excluindo $0).
  • $@: representa todos os argumentos como uma lista separada, preservando espaços internos.
#!/bin/bash
echo "Total de argumentos: $#"
echo "Argumentos: $@"

A diferença entre $@ e $* é sutil, mas crucial:

#!/bin/bash
# teste_aspas.sh
echo "Com \$@:"
for arg in "$@"; do
    echo "  [$arg]"
done

echo "Com \$*:"
for arg in $*; do
    echo "  [$arg]"
done

Execute: ./teste_aspas.sh "João Silva" "Maria"

Com $@:
  [João Silva]
  [Maria]
Com $*:
  [João]
  [Silva]
  [Maria]

Sempre use "$@" com aspas duplas para preservar argumentos com espaços. $* sem aspas quebra cada palavra separadamente.

3. O Comando shift: Removendo o Primeiro Argumento

O comando shift é uma ferramenta poderosa para manipular argumentos posicionais. Quando executado, ele descarta $1 e move todos os argumentos uma posição para a esquerda: o antigo $2 torna-se $1, $3 torna-se $2, e assim por diante.

#!/bin/bash
echo "Antes do shift: $1 $2 $3"
shift
echo "Depois do shift: $1 $2 $3"

Execute: ./shift_simples.sh um dois tres

Antes do shift: um dois tres
Depois do shift: dois tres

A sintaxe avançada shift N permite descartar múltiplos argumentos de uma só vez:

#!/bin/bash
echo "Antes: $1 $2 $3 $4"
shift 2
echo "Depois: $1 $2 $3 $4"

Execute: ./shift_multiplo.sh a b c d

Antes: a b c d
Depois: c d

Quando não há mais argumentos para deslocar, shift retorna um código de saída 1 (falha), mas não interrompe o script automaticamente. Por isso, é recomendável verificar $# antes de usar shift em loops.

4. Iterando sobre Argumentos com Loops e shift

O uso combinado de shift com loops while é uma técnica clássica para processar argumentos sequencialmente:

#!/bin/bash
# soma.sh - Soma todos os números fornecidos
soma=0
while [ $# -gt 0 ]; do
    soma=$((soma + $1))
    shift
done
echo "Soma total: $soma"

Execute: ./soma.sh 5 10 15

Soma total: 30

Alternativamente, o loop for com "$@" oferece uma abordagem mais limpa quando não precisamos modificar os argumentos:

#!/bin/bash
soma=0
for num in "$@"; do
    soma=$((soma + num))
done
echo "Soma total: $soma"

A escolha entre while com shift e for com "$@" depende do contexto: use shift quando precisar consumir argumentos em pares (ex.: --nome valor) ou quando a ordem de processamento exigir descartes seletivos.

5. Tratamento de Opções e Flags Simples

Combinando case, $1 e shift, podemos interpretar flags como -v (verbose) ou --help:

#!/bin/bash
# backup.sh - Script de backup com opções
verbose=0
destino="./backup"

while [ $# -gt 0 ]; do
    case "$1" in
        -v|--verbose)
            verbose=1
            shift
            ;;
        -d|--destino)
            destino="$2"
            shift 2
            ;;
        -h|--help)
            echo "Uso: $0 [-v] [-d DIR] arquivos..."
            exit 0
            ;;
        *)
            break  # Sai do loop, argumentos restantes são arquivos
            ;;
    esac
done

echo "Modo verbose: $verbose"
echo "Diretório destino: $destino"
echo "Arquivos: $@"

Execute: ./backup.sh -v -d /tmp/backup doc1.txt doc2.txt

Modo verbose: 1
Diretório destino: /tmp/backup
Arquivos: doc1.txt doc2.txt

Armadilha comum: opções agrupadas como -abc não são interpretadas automaticamente. Para isso, seria necessário um parser mais sofisticado (como getopts).

6. Validação e Boas Práticas com Argumentos

Sempre valide se os argumentos obrigatórios foram fornecidos:

#!/bin/bash
usage() {
    echo "Uso: $0 <nome_arquivo> <diretorio_destino>"
    echo "Exemplo: $0 relatorio.pdf /tmp/backup"
    exit 1
}

if [ $# -lt 2 ]; then
    usage
fi

arquivo="$1"
destino="$2"
echo "Copiando $arquivo para $destino..."

Boas práticas adicionais:
- Use aspas duplas em "$1", "$2" e "$@" para preservar espaços.
- Defina uma função usage() clara para orientar o usuário.
- Evite assumir ordem fixa: documente ou use flags nomeadas.
- Verifique $# antes de acessar qualquer argumento posicional.

7. Argumentos com Espaços e Caracteres Especiais

Arquivos com espaços ou caracteres especiais exigem atenção redobrada:

#!/bin/bash
# processa_arquivos.sh
echo "Processando arquivos:"
for arquivo in "$@"; do
    if [ -f "$arquivo" ]; then
        echo "  OK: $arquivo"
    else
        echo "  ERRO: $arquivo não encontrado"
    fi
done

Execute: ./processa_arquivos.sh "meu arquivo.txt" "outro arquivo.pdf"

Processando arquivos:
  OK: meu arquivo.txt
  OK: outro arquivo.pdf

Sem as aspas duplas, cada espaço quebraria o nome do arquivo em argumentos separados. Caracteres como * e ? também são seguros quando protegidos por aspas, evitando expansão prematura de globs.

8. Exemplos Práticos e Casos de Uso

Script de saudação personalizada:

#!/bin/bash
# saudacao.sh
if [ $# -lt 2 ]; then
    echo "Uso: $0 <nome> <mensagem>"
    exit 1
fi
echo "Olá $1! $2"

Execute: ./saudacao.sh "Maria" "Bem-vinda ao curso"

Olá Maria! Bem-vinda ao curso

Script de renomeação em lote:

#!/bin/bash
# renomear.sh
prefixo="$1"
shift
for arquivo in "$@"; do
    mv "$arquivo" "${prefixo}_${arquivo}"
    echo "Renomeado: $arquivo -> ${prefixo}_${arquivo}"
done

Execute: ./renomear.sh "foto" "praia.jpg" "cachorro.jpg"

Renomeado: praia.jpg -> foto_praia.jpg
Renomeado: cachorro.jpg -> foto_cachorro.jpg

Script de calculadora simples:

#!/bin/bash
# calc.sh
if [ $# -ne 3 ]; then
    echo "Uso: $0 <operacao> <num1> <num2>"
    echo "Operações: add, sub, mul, div"
    exit 1
fi

op="$1"
n1="$2"
n2="$3"

case "$op" in
    add) echo $((n1 + n2)) ;;
    sub) echo $((n1 - n2)) ;;
    mul) echo $((n1 * n2)) ;;
    div)
        if [ "$n2" -eq 0 ]; then
            echo "Erro: divisão por zero"
            exit 1
        fi
        echo $((n1 / n2))
        ;;
    *) echo "Operação inválida: $op"; exit 1 ;;
esac

Execute: ./calc.sh add 10 20

30

Dominar $1, $2, $@ e shift é essencial para criar scripts Bash flexíveis e profissionais. Esses mecanismos permitem que seus scripts interajam dinamicamente com o usuário, processem listas de arquivos, interpretem flags de configuração e se adaptem a diferentes cenários de uso. Pratique combinando validação, loops e tratamento cuidadoso de espaços para construir ferramentas de linha de comando robustas e reutilizáveis.

Referências