Em shell script, os parâmetros posicionais são variáveis especiais que permitem acessar os argumentos passados para um script ou função na linha de comando. Eles são fundamentais para criar scripts flexíveis e reutilizáveis, que podem receber entradas do usuário ou de outros programas. Nesta aula, vamos explorar as principais variáveis relacionadas a parâmetros posicionais: $1, $2, $@, $*, $# e o comando shift.

Dominar esses conceitos é essencial para qualquer desenvolvedor que deseja automatizar tarefas no Linux, pois permite que scripts se comportem de maneira dinâmica com base nos argumentos fornecidos. Veremos como cada um funciona, suas diferenças sutis e como usá-los em conjunto.

$1, $2

As variáveis $1, $2, $3, etc., representam o primeiro, segundo, terceiro argumento passado para o script ou função. Elas são chamadas de parâmetros posicionais porque sua posição na linha de comando determina qual variável recebe o valor. Por exemplo, se um script é chamado com ./script.sh foo bar, então $1 será "foo" e $2 será "bar".

É importante notar que $0 é uma variável especial que contém o nome do próprio script (ou da função, dependendo do contexto). Para acessar argumentos além do nono, é necessário usar chaves, como $ {10}, $ {11}, etc. Sem as chaves, o shell interpretaria $10 como $1 seguido de "0".

#!/bin/bash
# Exemplo de uso de $1 e $2
echo "Nome do script: $0"
echo "Primeiro argumento: $1"
echo "Segundo argumento: $2"
# Exemplo com argumento acima de 9
echo "Décimo argumento: ${10}"

Ao executar ./script.sh um dois tres quatro cinco seis sete oito nove dez, a saída será:

Nome do script: ./script.sh
Primeiro argumento: um
Segundo argumento: dois
Décimo argumento: dez

$@ e $*

Ambos $@ e $* representam todos os argumentos passados para o script, mas com uma diferença importante quando usados dentro de aspas duplas. $@ preserva cada argumento como uma palavra separada, mesmo que contenha espaços, enquanto $* combina todos os argumentos em uma única string, separada pelo primeiro caractere do IFS (normalmente espaço).

Na prática, "$@" é mais seguro e é a forma recomendada para iterar sobre os argumentos, pois mantém a integridade de cada um. Já "$*" pode causar problemas se algum argumento contiver espaços. Veja o exemplo:

#!/bin/bash
echo "Usando \"\$@\":"
for arg in "$@"; do
    echo "  - $arg"
done
echo "Usando \"\$*\":"
for arg in "$*"; do
    echo "  - $arg"
done

Se o script for chamado com ./script.sh "um dois" tres, a saída será:

Usando "$@":
  - um dois
  - tres
Usando "$*":
  - um dois tres

Note que com "$*", todos os argumentos foram tratados como uma única string, perdendo a separação original.

$#

A variável $# contém o número de argumentos passados para o script (excluindo o nome do script). É útil para verificar se o usuário forneceu a quantidade correta de argumentos antes de prosseguir com a execução, evitando erros.

Exemplo de uso:

#!/bin/bash
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"
if [ $# -ge 2 ]; then
    echo "Segundo argumento: $2"
fi

Se o script for executado sem argumentos, ele exibe uma mensagem de erro e sai com código 1. Caso contrário, mostra os argumentos conforme disponíveis.

shift

O comando shift desloca os parâmetros posicionais para a esquerda, descartando o primeiro argumento ($1) e movendo os demais uma posição para baixo: o antigo $2 torna-se $1, $3 torna-se $2, e assim por diante. Opcionalmente, pode-se passar um número para shift, indicando quantas posições deslocar (ex.: shift 2 descarta os dois primeiros argumentos).

O shift é frequentemente usado em loops para processar argumentos um a um, especialmente quando o script aceita opções ou flags. Exemplo:

#!/bin/bash
echo "Argumentos recebidos: $@"
while [ $# -gt 0 ]; do
    echo "Processando: $1"
    shift
done
echo "Todos os argumentos foram processados."

Executando ./script.sh a b c, a saída será:

Argumentos recebidos: a b c
Processando: a
Processando: b
Processando: c
Todos os argumentos foram processados.

Note que após o loop, $# é zero e os parâmetros posicionais foram esgotados.

Boas práticas

  • Sempre use aspas duplas ao expandir $@ ("$@") para preservar argumentos com espaços.
  • Verifique $# antes de usar $1, $2, etc., para evitar erros de argumentos faltantes.
  • Use shift com cuidado: faça uma cópia dos argumentos se precisar deles depois.
  • Para argumentos opcionais, use valores padrão com $ {1:-default}.

Referências

Exercícios

  1. Escreva um script que receba dois números como argumentos e imprima a soma deles. Se não forem fornecidos exatamente dois argumentos, exiba uma mensagem de erro.

    ✓ Resposta:
    #!/bin/bash
    if [ $# -ne 2 ]; then
        echo "Erro: São necessários exatamente 2 argumentos."
        echo "Uso: $0 <num1> <num2>"
        exit 1
    fi
    soma=$(( $1 + $2 ))
    echo "A soma de $1 e $2 é $soma."
    
  2. Crie um script que imprima todos os argumentos passados, um por linha, usando "$@".

    ✓ Resposta:
    #!/bin/bash
    echo "Lista de argumentos:"
    for arg in "$@"; do
        echo "$arg"
    done
    
  3. Faça um script que exiba o número de argumentos e o primeiro e último argumento (dica: use shift para chegar ao último).

    ✓ Resposta:
    #!/bin/bash
    if [ $# -eq 0 ]; then
        echo "Nenhum argumento fornecido."
        exit 1
    fi
    primeiro=$1
    # Desloca até o último argumento
    while [ $# -gt 1 ]; do
        shift
    done
    ultimo=$1
    echo "Número de argumentos: $#"
    echo "Primeiro argumento: $primeiro"
    echo "Último argumento: $ultimo"
    
  4. Escreva um script que aceite uma lista de nomes de arquivos e exiba apenas aqueles que existem no diretório atual. Use shift para processar cada argumento.

    ✓ Resposta:
    #!/bin/bash
    if [ $# -eq 0 ]; then
        echo "Nenhum arquivo especificado."
        exit 1
    fi
    echo "Arquivos existentes:"
    while [ $# -gt 0 ]; do
        if [ -f "$1" ]; then
            echo "$1"
        fi
        shift
    done
    
  5. Crie um script que aceite opções: -v para modo verboso e -n <nome> para definir um nome. Use shift para processar as opções. Se nenhuma opção for passada, exiba uma mensagem padrão.

    ✓ Resposta:
    #!/bin/bash
    verbose=false
    nome=""
    while [ $# -gt 0 ]; do
        case "$1" in
            -v)
                verbose=true
                shift
                ;;
            -n)
                if [ $# -lt 2 ]; then
                    echo "Erro: -n requer um argumento."
                    exit 1
                fi
                nome="$2"
                shift 2
                ;;
            *)
                echo "Opção desconhecida: $1"
                exit 1
                ;;
        esac
    done
    if [ "$verbose" = true ]; then
        echo "Modo verboso ativado."
    fi
    if [ -n "$nome" ]; then
        echo "Olá, $nome!"
    else
        echo "Olá, mundo!"
    fi