fbpx Advanced Bash: Functions, Arrays, and Error Handling Guide | Certificação Linux

Advanced Shell Scripting: Functions, Arrays, Error Handling, and Best Practices

📅 07/19/2026 ⏱ 4 min ✍️ Uira Ribeiro
Advanced Shell Scripting: Functions, Arrays, Error Handling, and Best Practices
Exam Pass App — mais de 7.000 questões LPI, offline, em 5 idiomas, disponível no Android e na App Store
Available on
Google Play
Available on
App Store

Bash scripts are the glue of Linux systems administration. But there's a huge difference between a script that works once and a script that can run in production at 3am without blowing up the server. This article covers the techniques that make that difference.

The Mandatory Header for Every Serious Script

#!/usr/bin/env bash
set -euo pipefail

# -e : sai imediatamente se qualquer comando retornar erro
# -u : trata variáveis não definidas como erro (evita $VAZIO virar "")
# -o pipefail : propaga erro em pipes (cmd1 | cmd2 → falha se cmd1 falha)

Without these three flags, your script continues silently after a failed rm -rf or an empty variable that destroys a file path.

Functions

#!/usr/bin/env bash
set -euo pipefail

# Convenção: funções de log com timestamp
log()  { echo "[$(date '+%Y-%m-%d %H:%M:%S')] INFO:  $*"; }
warn() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] WARN:  $*" >&2; }
die()  { echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERRO:  $*" >&2; exit 1; }

# Função com retorno de valor
soma() {
    local a=$1 b=$2     # local: variável não vaza para o escopo global
    echo $(( a + b ))
}

resultado=$(soma 3 7)
log "3 + 7 = $resultado"

# Função que verifica dependências
requer() {
    for cmd in "$@"; do
        command -v "$cmd" &>/dev/null || die "Comando não encontrado: $cmd"
    done
}

requer curl jq aws

Arrays

#!/usr/bin/env bash
set -euo pipefail

# Array indexado
servidores=("web01" "web02" "db01" "cache01")

# Iterar
for srv in "${servidores[@]}"; do
    echo "Verificando $srv..."
done

# Número de elementos
echo "Total: ${#servidores[@]}"

# Slice (índices 1 e 2)
echo "${servidores[@]:1:2}"   # → web02 db01

# Array associativo (dicionário)
declare -A config
config[host]="localhost"
config[porta]="5432"
config[banco]="producao"

for chave in "${!config[@]}"; do
    echo "$chave = ${config[$chave]}"
done

# Ler linhas de um arquivo para array
mapfile -t linhas < /etc/hosts
echo "Linhas em /etc/hosts: ${#linhas[@]}"

Processing Arguments with getopts

#!/usr/bin/env bash
set -euo pipefail

uso() {
    cat <<EOF
Uso: $0 [-h] [-v] [-e AMBIENTE] [-n NOME]

  -h           Mostra esta ajuda
  -v           Modo verbose
  -e AMBIENTE  Ambiente (dev|stg|prod)   [obrigatório]
  -n NOME      Nome do deploy            [padrão: app]
EOF
    exit 1
}

VERBOSE=0
AMBIENTE=""
NOME="app"

while getopts ":hve:n:" opt; do
    case $opt in
        h) uso ;;
        v) VERBOSE=1 ;;
        e) AMBIENTE=$OPTARG ;;
        n) NOME=$OPTARG ;;
        :) die "Opção -$OPTARG requer argumento" ;;
        ?) die "Opção inválida: -$OPTARG" ;;
    esac
done

[[ -z "$AMBIENTE" ]] && die "-e AMBIENTE é obrigatório"
[[ "$AMBIENTE" =~ ^(dev|stg|prod)$ ]] || die "Ambiente inválido: $AMBIENTE"

echo "Deploy de '$NOME' em '$AMBIENTE'"
[[ $VERBOSE -eq 1 ]] && echo "[verbose] Detalhes adicionais aqui"

Error Handling with trap

#!/usr/bin/env bash
set -euo pipefail

TMPDIR=$(mktemp -d)

# Cleanup automático ao sair (com erro ou não)
cleanup() {
    local codigo=$?
    rm -rf "$TMPDIR"
    [[ $codigo -ne 0 ]] && echo "Script falhou com código $codigo" >&2
}
trap cleanup EXIT

# Se o script morrer por sinal (Ctrl+C, kill), limpar antes
trap 'echo "Interrompido" >&2; exit 130' INT TERM

echo "Arquivos temporários em $TMPDIR"
# ... seu trabalho aqui ...
# TMPDIR é removido automaticamente ao sair

Logging to File

#!/usr/bin/env bash
set -euo pipefail

LOGFILE="/var/log/meu-script.log"
LOGDIR=$(dirname "$LOGFILE")
mkdir -p "$LOGDIR"

# Redirecionar stdout e stderr para o log E para o terminal
exec > >(tee -a "$LOGFILE") 2>&1

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }

log "=== Iniciando script $(basename "$0") ==="
log "Usuário: $(whoami) | PID: $$"

Essential Security Checks

# Verificar se está rodando como root
[[ $EUID -eq 0 ]] || die "Este script precisa ser root"

# Verificar se arquivo existe antes de usar
[[ -f "$CONFIG" ]] || die "Arquivo não encontrado: $CONFIG"

# Evitar race condition com lock file
LOCKFILE="/tmp/meu-script.lock"
exec 200>"$LOCKFILE"
flock -n 200 || die "Outra instância já está rodando"

# Validar que variável não está vazia E é numérica
[[ "${PORTA:-}" =~ ^[0-9]+$ ]] || die "PORTA deve ser numérica"

# Aspas duplas SEMPRE em variáveis (evita word splitting)
arquivo="meu arquivo com espaços.txt"
rm -f "$arquivo"   # correto
# rm -f $arquivo   # errado: vira 'rm -f meu arquivo com espaços.txt' → 4 args

Production Script Template

#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
LOGFILE="/var/log/$(basename "$0" .sh).log"

exec > >(tee -a "$LOGFILE") 2>&1

log()  { echo "[$(date '+%F %T')] INFO  $*"; }
warn() { echo "[$(date '+%F %T')] WARN  $*" >&2; }
die()  { echo "[$(date '+%F %T')] ERRO  $*" >&2; exit 1; }

cleanup() { log "Finalizado (código: $?)"; }
trap cleanup EXIT

main() {
    log "Iniciando — args: $*"
    # seu código aqui
}

main "$@"

This template already has logging, automatic cleanup, error propagation, and argument passing to the main function. Copy, adapt, and use it as a base for any new script.

Uira Ribeiro

Prof. Uirá Ribeiro

Chair do Board do Linux Professional Institute

After years of watching professionals waste hours on simple tasks because they didn't know the right Linux tools, I decided to teach.
Today I'm the author of most of LPI's official Learning Materials and mentor to over 14,000 certified students.

See my full story →

That was just one piece. The course teaches the whole path — from your first command to exam day.

Certificação Linux — a plataforma líder em certificações Linux no Brasil
WhatsApp