Funções são blocos de código reutilizáveis que permitem organizar scripts em partes lógicas, evitar repetição e facilitar a manutenção. No Bash, uma função é definida uma vez e pode ser chamada inúmeras vezes, recebendo argumentos e retornando valores. Dominar funções é essencial para escrever scripts robustos e legíveis.

Nesta aula, vamos explorar os conceitos fundamentais: como definir uma função, como chamá-la, como passar e acessar parâmetros usando $1, $2, etc., e a diferença entre return e exit para controle de status. Ao final, você terá os conhecimentos para modularizar seus scripts de forma eficiente.

Definição

Em Bash, existem duas sintaxes principais para definir uma função. A primeira usa a palavra-chave function seguida do nome e parênteses opcionais. A segunda omite a palavra-chave e usa apenas o nome seguido de parênteses. Ambas são equivalentes, mas a segunda é mais portável (POSIX).

O corpo da função deve estar entre chaves { }, e é boa prática incluir espaços após a chave de abertura e antes da de fechamento. Dentro do corpo, você pode colocar qualquer comando válido do shell.

# Sintaxe 1: com 'function'
function saudacao {
    echo "Olá, mundo!"
}

# Sintaxe 2: sem 'function' (recomendada)
saudacao() {
    echo "Olá, mundo!"
}

Observe que os parênteses após o nome são obrigatórios na segunda forma, mas opcionais na primeira. O nome da função deve seguir as mesmas regras de identificadores do shell: começar com letra ou sublinhado, seguido de letras, números ou sublinhados. Evite nomes de comandos existentes para não sobrescrevê-los.

Chamada

Para executar uma função, basta escrever seu nome como se fosse um comando. A chamada pode ser feita antes ou depois da definição, mas é comum definir todas as funções no início do script para legibilidade. Se a função for chamada antes de ser definida, o Bash emitirá um erro de comando não encontrado, pois as funções são carregadas quando a definição é executada.

#!/bin/bash

# Definição
cumprimentar() {
    echo "Bem-vindo ao script!"
}

# Chamada
cumprimentar

As funções podem ser chamadas dentro de outras funções, em condicionais, loops e até mesmo em substituições de comando. Elas são executadas no mesmo shell (a menos que você use subshell), portanto podem alterar variáveis globais e o diretório atual.

Parâmetros ($1 dentro da função)

Assim como scripts recebem parâmetros posicionais ($1, $2, ...), funções também podem receber argumentos. Dentro da função, $1 refere-se ao primeiro argumento passado na chamada, $2 ao segundo, e assim por diante. $# indica o número de argumentos, e $@ ou $* representam todos os argumentos. Esses parâmetros são locais à função, ou seja, não interferem nos parâmetros do script.

#!/bin/bash

imprimir_nome() {
    echo "Primeiro nome: $1"
    echo "Segundo nome: $2"
    echo "Total de argumentos: $#"
    echo "Todos: $@"
}

imprimir_nome "Maria" "Silva"

É importante usar aspas ao passar argumentos que contenham espaços. Dentro da função, você pode usar shift para descartar o primeiro argumento e promover os demais. Para evitar conflitos com variáveis globais, é recomendável declarar variáveis locais com local.

soma() {
    local a=$1
    local b=$2
    echo $((a + b))
}

resultado=$(soma 5 3)
echo "Soma: $resultado"

return e exit codes

O comando return termina a execução da função e retorna um código de status (0 a 255). Esse código pode ser usado pelo chamador para saber se a função teve sucesso (0) ou falhou (qualquer valor diferente de 0). Por padrão, se você não usar return, a função retorna o status do último comando executado.

verificar_arquivo() {
    if [[ -f "$1" ]]; then
        return 0
    else
        return 1
    fi
}

verificar_arquivo "/etc/passwd"
if [[ $? -eq 0 ]]; then
    echo "Arquivo existe."
else
    echo "Arquivo não encontrado."
fi

Já o comando exit termina o script inteiro, não apenas a função. Ao usar exit N, o script encerra com o código de saída N. Isso é útil para abortar a execução em caso de erro grave. Lembre-se de que return só pode ser usado dentro de funções ou scripts quando você usa source; fora deles, return causa erro.

# Exemplo de exit dentro de função
falha_fatal() {
    echo "Erro crítico!"
    exit 1
}

echo "Início"
falha_fatal
echo "Isto não será impresso"

Uma diferença crucial: return devolve o controle ao chamador, permitindo que o script continue. exit encerra o processo imediatamente. Use return para indicar sucesso ou falha de uma operação, e exit apenas para terminar o script prematuramente.

Boas práticas

  • Defina todas as funções no início do script ou em um arquivo separado que você inclui com source.
  • Use nomes descritivos e evite colisões com comandos do sistema.
  • Declare variáveis locais com local para evitar efeitos colaterais globais.
  • Valide os argumentos dentro da função, verificando $# e valores esperados.
  • Documente cada função com comentários explicando o que ela faz e quais parâmetros espera.

Exercícios

  1. Escreva uma função chamada eh_par que recebe um número como argumento e retorna 0 se for par, 1 se for ímpar. Depois, chame-a para o número 4 e imprima a mensagem adequada usando $?.

    ✓ Resposta:
    #!/bin/bash
    
    eh_par() {
        local numero=$1
        if (( numero % 2 == 0 )); then
            return 0
        else
            return 1
        fi
    }
    
    eh_par 4
    if [[ $? -eq 0 ]]; then
        echo "4 é par"
    else
        echo "4 é ímpar"
    fi
    
  2. Crie uma função concatena que recebe duas strings e as concatena com um espaço entre elas, imprimindo o resultado. Teste com "Olá" e "mundo".

    ✓ Resposta:
    #!/bin/bash
    
    concatena() {
        echo "$1 $2"
    }
    
    concatena "Olá" "mundo"
    
  3. Explique a diferença entre return 1 e exit 1 dentro de uma função e dê um exemplo de quando usar cada um.

    ✓ Resposta: return encerra a função e devolve o controle ao chamador, permitindo que o script continue. exit encerra todo o script imediatamente. Use return para indicar sucesso ou falha de uma operação; use exit para abortar o script em caso de erro fatal. Exemplo: se uma função de configuração falhar, você pode usar return 1 para que o chamador decida o que fazer; se um erro crítico acontecer, use exit 1 para parar o script.
  4. Escreva uma função maior que recebe dois números e retorna o maior deles usando return (não pode usar echo). Depois, chame-a e capture o valor retornado com $?.

    ✓ Resposta:
    #!/bin/bash
    
    maior() {
        if (( $1 > $2 )); then
            return $1
        else
            return $2
        fi
    }
    
    maior 10 20
    resultado=$?
    echo "O maior é: $resultado"
    
  5. Crie um script que define uma função menu que exibe opções (1-3) e lê a escolha do usuário. A função deve retornar 0 se a opção for válida e 1 se inválida. Teste chamando a função.

    ✓ Resposta:
    #!/bin/bash
    
    menu() {
        echo "Escolha uma opção:"
        echo "1 - Opção A"
        echo "2 - Opção B"
        echo "3 - Sair"
        read -p "Digite: " opcao
        case $opcao in
            1|2|3) return 0 ;;
            *) return 1 ;;
        esac
    }
    
    menu
    if [[ $? -eq 0 ]]; then
        echo "Opção válida"
    else
        echo "Opção inválida"
    fi
    

Referências