Declarando funções

Existem duas formas de declarar funções no Bash. Ambas são equivalentes:

# Forma 1: com a palavra-chave function
function saudar {
  echo "Olá, $1!"
}

# Forma 2: com parênteses (mais portável, compatível com POSIX sh)
saudar() {
  echo "Olá, $1!"
}

saudar "admin"
Olá, admin!

Parâmetros posicionais

Dentro de uma função, $1, $2, etc. referem-se aos argumentos passados para a função (não para o script).

criar_usuario() {
  local NOME="$1"
  local GRUPO="$2"
  local SHELL="${3:-/bin/bash}"  # padrão: /bin/bash

  echo "Criando usuário: $NOME"
  echo "  Grupo: $GRUPO"
  echo "  Shell: $SHELL"
  echo "  Total de argumentos: $#"
}

criar_usuario "joao" "devops" "/bin/zsh"
criar_usuario "maria" "sre"
Criando usuário: joao
  Grupo: devops
  Shell: /bin/zsh
  Total de argumentos: 3
Criando usuário: maria
  Grupo: sre
  Shell: /bin/bash
  Total de argumentos: 2

return

O return define o código de saída da função (0-255). Não é para retornar dados — para isso, use echo e capture com $().

usuario_existe() {
  id "$1" &> /dev/null
  return $?  # propaga o código de saída do id
}

if usuario_existe "root"; then
  echo "Usuário root existe"
fi
Usuário root existe

Para retornar valores, use echo:

obter_ip() {
  local IFACE="$1"
  ip -4 addr show "$IFACE" | grep -oP '(?<=inet\s)\d+(\.\d+){3}'
}

MEU_IP=$(obter_ip "eth0")
echo "IP da eth0: $MEU_IP"
IP da eth0: 192.168.1.50

local e escopo de variáveis

Sem local, variáveis dentro de funções são globais. Isso causa bugs sutis.

MSG="global"

sem_local() {
  MSG="modificada dentro da função"
}

com_local() {
  local MSG="só existe aqui"
  echo "Dentro: $MSG"
}

sem_local
echo "Após sem_local: $MSG"

com_local
echo "Após com_local: $MSG"
Dentro: só existe aqui
Após sem_local: modificada dentro da função
Após com_local: modificada dentro da função
Dica: Sempre use local para variáveis dentro de funções. Isso evita colisões com variáveis globais e torna o código mais previsível.

Funções como comandos

Funções se comportam como comandos comuns. Você pode usá-las em pipes, redirecionar saída e combiná-las com outros comandos.

listar_servicos_ativos() {
  systemctl list-units --type=service --state=running --no-legend \
    | awk '{print $1}'
}

# Usando em pipe
listar_servicos_ativos | grep ssh

# Redirecionando saída
listar_servicos_ativos > /tmp/servicos_ativos.txt

# Contando resultados
echo "Serviços ativos: $(listar_servicos_ativos | wc -l)"

Um padrão útil é criar uma biblioteca de funções em um arquivo separado e carregá-la com source:

# /usr/local/lib/utils.sh
log_info()  { echo "[INFO]  $(date +%H:%M:%S) $*"; }
log_warn()  { echo "[WARN]  $(date +%H:%M:%S) $*"; }
log_error() { echo "[ERROR] $(date +%H:%M:%S) $*" >&2; }

# No seu script:
source /usr/local/lib/utils.sh
log_info "Backup iniciado"
log_warn "Espaço em disco baixo"
log_error "Falha na conexão"
[INFO]  14:32:07 Backup iniciado
[WARN]  14:32:07 Espaço em disco baixo
[ERROR] 14:32:07 Falha na conexão
Dica: Use declare -F para listar todas as funções definidas no shell atual. Útil para depuração e para verificar se uma função de biblioteca foi carregada corretamente.