kb.erickguedes.com
Bash e Shell Script: Automação Unix/Linux

Controle de Fluxo e Funções

Aula 2 de 6

Condicionais: if / test / [[

test e [ ]

# Sintaxe: test <expressão> ou [ <expressão> ]

# Testes de arquivo
test -f "arquivo.txt" && echo "É arquivo regular"
[ -d "diretorio" ] && echo "É diretório"
[ -e "caminho" ] && echo "Existe"
[ -r "arquivo" ] && echo "Legível"
[ -w "arquivo" ] && echo "Gravável"
[ -x "script.sh" ] && echo "Executável"
[ -s "arquivo" ] && echo "Tamanho > 0"
[ -L "link" ] && echo "É symlink"

[[ ]] — Versão Moderna

# [[ ]] é mais seguro e poderoso que [ ]

# Strings
[[ "$nome" == "Maria" ]] && echo "Igual"
[[ "$nome" != "João" ]] && echo "Diferente"
[[ -z "$vazia" ]] && echo "String vazia"
[[ -n "$nome" ]] && echo "String não vazia"
[[ "hello" == h* ]] && echo "Glob match"

# Numérico (com -eq, -ne, -lt, -le, -gt, -ge)
[[ $idade -ge 18 ]] && echo "Maior de idade"
[[ $qtde -gt 0 ]] && echo "Maior que zero"

# Operadores lógicos
[[ -f "$file" && -r "$file" ]] && echo "Arquivo legível"
[[ "$user" == "root" || $UID -eq 0 ]] && echo "Acesso root"

# Regex (operador =~)
[[ "$email" =~ ^[a-z]+@[a-z]+\.[a-z]{2,}$ ]] && echo "Email válido"

if / elif / else

if [[ $# -eq 0 ]]; then
    echo "Nenhum argumento"
elif [[ $# -eq 1 ]]; then
    echo "Um argumento: $1"
else
    echo "Múltiplos argumentos: $@"
fi

case

case "$1" in
    start)
        echo "Iniciando serviço..."
        ;;
    stop)
        echo "Parando serviço..."
        ;;
    restart|reload)
        echo "Reiniciando..."
        ;;
    [0-9][0-9])
        echo "Número de dois dígitos: $1"
        ;;
    *.txt)
        echo "Arquivo de texto: $1"
        ;;
    "")
        echo "Argumento vazio"
        ;;
    *)
        echo "Opção desconhecida: $1"
        exit 1
        ;;
esac

Fallthrough (;&)

# ;& permite continuar para o próximo padrão
case "$nivel" in
    alto)
        echo "Nível alto"
        ;&
    medio)
        echo "Nível médio"
        ;&
    baixo)
        echo "Nível baixo"
        ;;
esac

Loops

for — C-style

# Sintaxe clássica C
for ((i = 0; i < 10; i++)); do
    echo "Iteração $i"
done

# Decremento
for ((i = 10; i > 0; i--)); do
    echo "Contagem regressiva: $i"
done

# Passo diferente
for ((i = 0; i <= 100; i += 10)); do
    echo "$i%"
done

for — for-in

# Lista explícita
for cor in vermelho azul verde; do
    echo "Cor: $cor"
done

# Globbing
for file in *.txt; do
    echo "Processando: $file"
done

# Sequência com seq
for i in $(seq 1 5); do
    echo "$i"
done

# Brace expansion
for i in {1..10}; do
    square=$((i * i))
    echo "$i² = $square"
done

# Array
frutas=("maçã" "banana" "laranja")
for fruta in "${frutas[@]}"; do
    echo "Fruta: $fruta"
done

while

# Loop com contador
count=0
while [[ $count -lt 5 ]]; do
    echo "Contagem: $count"
    ((count++))
done

# Ler arquivo linha a linha
while IFS= read -r linha; do
    echo "Linha: $linha"
done < "arquivo.txt"

# Loop infinito (com break)
while true; do
    read -p "Comando: " cmd
    [[ "$cmd" == "sair" ]] && break
    echo "Executando: $cmd"
done

until

# Executa ENQUANTO condição é falsa
contador=0
until [[ $contador -ge 5 ]]; do
    echo "Contagem: $contador"
    ((contador++))
done
# 0, 1, 2, 3, 4

break e continue

for i in {1..10}; do
    [[ $i -eq 3 ]] && continue   # pula o 3
    [[ $i -eq 8 ]] && break      # para no 8
    echo "$i"
done
# 1 2 4 5 6 7

Funções

Definição

# Sintaxe 1 (recomendada)
minha_funcao() {
    local name="$1"
    echo "Olá, $name!"
}

# Sintaxe 2 (function explícito)
function minha_funcao {
    echo "Olá!"
}

Argumentos e Local

calculadora() {
    local a="$1"
    local op="$2"
    local b="$3"

    case "$op" in
        +) echo $((a + b)) ;;
        -) echo $((a - b)) ;;
        \*) echo $((a * b)) ;;
        /) echo $((a / b)) ;;
        *) echo "Operador inválido"; return 1 ;;
    esac
}

# Chamar
resultado=$(calculadora 10 + 5)
echo "Resultado: $resultado"

return vs echo

# return: código de saída (0-255)
is_even() {
    local num=$1
    return $((num % 2))
}
is_even 4 && echo "Par"     # Par
is_even 3 || echo "Ímpar"   # Ímpar

# echo: retornar valor
get_username() {
    echo "$USER"
}
nome=$(get_username)
echo "Usuário: $nome"

source

# Arquivo lib.sh
cat > lib.sh << 'EOF'
log_info() { echo "[INFO] $*"; }
log_error() { echo "[ERROR] $*" >&2; }
EOF

# Script principal
cat > main.sh << 'EOF'
#!/bin/bash
source ./lib.sh  # ou . ./lib.sh

log_info "Script iniciado"
log_error "Algo deu errado"
EOF

Lab: Gerenciador de Tarefas via CLI

cat > task-manager.sh << 'SCRIPT'
#!/bin/bash
set -euo pipefail

TASKS_FILE="${HOME}/.tasks.txt"

tarefa_listar() {
    if [[ ! -f "$TASKS_FILE" ]]; then
        echo "Nenhuma tarefa encontrada."
        return
    fi
    local count=0
    while IFS= read -r task; do
        ((count++))
        echo "[$count] $task"
    done < "$TASKS_FILE"
}

tarefa_adicionar() {
    echo "$*" >> "$TASKS_FILE"
    echo "Tarefa adicionada: $*"
}

tarefa_remover() {
    local num="$1"
    local tmp=$(mktemp)
    local count=0

    while IFS= read -r task; do
        ((count++))
        [[ $count -eq $num ]] && continue
        echo "$task" >> "$tmp"
    done < "$TASKS_FILE"

    mv "$tmp" "$TASKS_FILE"
    echo "Tarefa $num removida."
}

case "${1:-}" in
    list)
        tarefa_listar
        ;;
    add)
        shift
        tarefa_adicionar "$*"
        ;;
    rm)
        tarefa_remover "$2"
        ;;
    *)
        echo "Uso: $0 {list|add <texto>|rm <num>}"
        exit 1
        ;;
esac
SCRIPT

chmod +x task-manager.sh

# Testar
./task-manager.sh add "Estudar bash"
./task-manager.sh add "Fazer exercícios"
./task-manager.sh list
./task-manager.sh rm 1

[[ ]] é mais seguro que [ ] — permite regex, glob e operadores lógicos. Use local em funções para isolar variáveis. return é para exit code, echo para valores.