DevOps Admin

Shell Scripting Bash: Variáveis, Condicionais, Laços e Funções na Prática Já leu

Variáveis em Bash: Fundamentos e Boas Práticas Variáveis são contêineres que armazenam dados na memória durante a execução de um script. Em Bash, não é necessário declarar o tipo de dado explicitamente — a linguagem realiza conversão dinâmica conforme o contexto. A sintaxe para atribuir uma variável é simples: , sem espaços antes ou depois do sinal de igualdade. Quando você precisa acessar o valor armazenado, utilize o símbolo precedendo o nome. A diferença entre usar e é sutil mas importante. A primeira forma funciona na maioria dos casos, mas a segunda é mais segura quando a variável é seguida de caracteres alfanuméricos. Por exemplo, se você tem e escreve , Bash procurará por uma variável chamada , não . Já retorna corretamente . ls -la Variáveis Especiais e Argumentos Bash fornece variáveis automáticas que recebem informações sobre o script e seus argumentos. A variável contém o nome do script, , , etc., armazenam o primeiro, segundo argumentos passados na

Variáveis em Bash: Fundamentos e Boas Práticas

Variáveis são contêineres que armazenam dados na memória durante a execução de um script. Em Bash, não é necessário declarar o tipo de dado explicitamente — a linguagem realiza conversão dinâmica conforme o contexto. A sintaxe para atribuir uma variável é simples: nome_variavel=valor, sem espaços antes ou depois do sinal de igualdade. Quando você precisa acessar o valor armazenado, utilize o símbolo $ precedendo o nome.

A diferença entre usar $variavel e ${variavel} é sutil mas importante. A primeira forma funciona na maioria dos casos, mas a segunda é mais segura quando a variável é seguida de caracteres alfanuméricos. Por exemplo, se você tem nome=João e escreve echo $nome_completo, Bash procurará por uma variável chamada nome_completo, não nome. Já echo ${nome}_completo retorna corretamente João_completo.

#!/bin/bash

# Atribuição simples
mensagem="Bem-vindo ao Bash"
idade=25
numero_pi=3.14159

# Acessando variáveis
echo "Mensagem: $mensagem"
echo "Idade: $idade"

# Forma segura com chaves
arquivo="dados"
echo "Arquivo: ${arquivo}.txt"  # dados.txt

# Variáveis de ambiente (já existem no sistema)
echo "Usuário atual: $USER"
echo "Diretório home: $HOME"
echo "Shell utilizado: $SHELL"

# Comando de substituição (armazena resultado de um comando)
data_atual=$(date +%d/%m/%Y)
echo "Data: $data_atual"

# Outro formato (mais antigo, menos preferido)
lista_arquivos=`ls -la`
echo "$lista_arquivos"

Variáveis Especiais e Argumentos

Bash fornece variáveis automáticas que recebem informações sobre o script e seus argumentos. A variável $0 contém o nome do script, $1, $2, etc., armazenam o primeiro, segundo argumentos passados na linha de comando, e $# indica a quantidade total de argumentos. A variável $@ expande para todos os argumentos, preservando espaços, enquanto $* expande como uma string única.

#!/bin/bash

# Script que processa argumentos
echo "Nome do script: $0"
echo "Número de argumentos: $#"
echo "Primeiro argumento: $1"
echo "Segundo argumento: $2"

# Todos os argumentos
echo "Todos os argumentos (\$@): $@"

# Valor de saída do comando anterior
resultado=$(echo "Teste")
echo "Status anterior: $?"  # 0 = sucesso, não-zero = erro

# Variáveis globais vs locais
variavel_global="Sou global"

funcao_teste() {
    local variavel_local="Sou local"
    echo "$variavel_global"     # Funciona
    echo "$variavel_local"      # Funciona
}

funcao_teste
# echo "$variavel_local"       # Erro: variável não existe aqui

Condicionais: Tomando Decisões no Script

Estruturas condicionais permitem que seu script execute diferentes blocos de código baseado em testes lógicos. A estrutura fundamental é o if, que avalia uma condição e executa um bloco se ela for verdadeira. Em Bash, uma condição é considerada verdadeira quando o comando retorna status 0, e falsa quando retorna qualquer valor diferente de zero. Você pode testar condições numéricas, de strings, de existência de arquivos e muito mais.

O Bash oferece dois formatos para testes: [ ] (mais portável e tradicional) e [[ ]] (mais moderno e seguro, específico do Bash). A forma [[ ]] trata melhor variáveis vazias e suporta expressões regulares, tornando-a preferível na maioria dos casos. Dentro dos colchetes, usamos operadores como -eq (igual numérico), -ne (não igual numérico), -lt (menor que), -gt (maior que), -z (string vazia) e -n (string não-vazia).

#!/bin/bash

idade=18

# Estrutura if-else simples
if [[ $idade -ge 18 ]]; then
    echo "Você é maior de idade"
else
    echo "Você é menor de idade"
fi

# Testando strings
nome="Maria"
if [[ $nome == "Maria" ]]; then
    echo "Olá, Maria!"
fi

# Testando se uma variável está vazia
entrada=""
if [[ -z $entrada ]]; then
    echo "Entrada está vazia"
fi

# Testando existência de arquivo
if [[ -f /etc/passwd ]]; then
    echo "Arquivo /etc/passwd existe"
fi

# Testando se é um diretório
if [[ -d /home ]]; then
    echo "/home é um diretório"
fi

# Múltiplas condições com AND (&&) e OR (||)
if [[ $idade -ge 18 ]] && [[ $nome == "Maria" ]]; then
    echo "Maria é maior de idade"
fi

if [[ $idade -lt 18 ]] || [[ $nome == "João" ]]; then
    echo "É menor de idade OU se chama João"
fi

Estruturas if-elif-else e Nested

Quando você precisa testar múltiplas condições sequenciais, use elif (else if). O script avalia cada condição na ordem até encontrar uma verdadeira. É importante notar que apenas o primeiro bloco verdadeiro é executado; os demais são ignorados. Para lógica mais complexa, você pode aninhar (nested) estruturas if dentro de outras.

#!/bin/bash

nota=7.5

if [[ $nota -ge 9 ]]; then
    echo "Conceito: A"
elif [[ $nota -ge 8 ]]; then
    echo "Conceito: B"
elif [[ $nota -ge 7 ]]; then
    echo "Conceito: C"
elif [[ $nota -ge 6 ]]; then
    echo "Conceito: D"
else
    echo "Conceito: F (Reprovado)"
fi

# Exemplo nested
idade=25
renda=3000

if [[ $idade -ge 18 ]]; then
    if [[ $renda -ge 2000 ]]; then
        echo "Aprovado para empréstimo"
    else
        echo "Renda insuficiente"
    fi
else
    echo "Maior de idade é requisito obrigatório"
fi

Case: Alternativa Limpa para Múltiplas Condições

Quando você precisa comparar uma variável contra vários valores fixos, a estrutura case é mais limpa e legível que múltiplos elif. Cada padrão (pattern) é testado sequencialmente, e o primeiro que combinar tem seu bloco executado. Use ;; para encerrar cada caso e *) como padrão padrão (equivalente ao else).

#!/bin/bash

dia=$(date +%A)

case $dia in
    Monday)
        echo "Início da semana de trabalho"
        ;;
    Friday)
        echo "Quase fim de semana!"
        ;;
    Saturday|Sunday)
        echo "Fim de semana - descanse!"
        ;;
    *)
        echo "Dia comum da semana"
        ;;
esac

# Case com padrões e variáveis
operacao=$1
numero1=$2
numero2=$3

case $operacao in
    add|somar)
        echo $((numero1 + numero2))
        ;;
    sub|subtrair)
        echo $((numero1 - numero2))
        ;;
    mul|multiplicar)
        echo $((numero1 * numero2))
        ;;
    *)
        echo "Operação desconhecida"
        ;;
esac

Laços: Iterando sobre Dados

Laços permitem executar um bloco de código múltiplas vezes. O Bash oferece várias estruturas: for, while e until. O for é ideal quando você conhece previamente quantas iterações serão necessárias ou quando itera sobre uma coleção de itens. O while continua executando enquanto uma condição for verdadeira, sendo útil quando a quantidade de iterações é desconhecida antecipadamente. O until funciona de forma inversa ao while, repetindo enquanto a condição for falsa.

Dentro de um laço, as palavras-chave break e continue oferecem controle fino. break encerra o laço imediatamente, enquanto continue pula para a próxima iteração. Essas construções evitam lógica complexa aninhada e tornam o código mais legível.

For: Iteração com Coleções

O for tradicional em Bash itera sobre uma lista de palavras. Cada iteração atribui a próxima palavra à variável de controle. A lista pode vir de uma expansão de glob (*.txt), de uma variável contendo múltiplas palavras, ou de um comando que produz várias linhas.

#!/bin/bash

# Iterando sobre uma lista explícita
for fruta in maçã banana laranja; do
    echo "Fruta: $fruta"
done

# Iterando sobre arquivos
for arquivo in *.txt; do
    if [[ -f $arquivo ]]; then
        echo "Processando: $arquivo"
        # wc -l "$arquivo"
    fi
done

# Iterando sobre resultado de um comando
for usuario in $(cat /etc/passwd | cut -d: -f1 | head -5); do
    echo "Usuário: $usuario"
done

# For com range numérico (Bash 4+)
for i in {1..5}; do
    echo "Número: $i"
done

# For com range numérico e passo
for i in {0..20..5}; do
    echo "Valor: $i"
done

# For com aritmética (C-style)
for ((i=1; i<=3; i++)); do
    echo "Iteração $i"
done

# Acessando índices em array
frutas=("maçã" "banana" "laranja" "uva")
for ((i=0; i<${#frutas[@]}; i++)); do
    echo "$i: ${frutas[$i]}"
done

While e Until: Iteração Condicional

while é útil quando a quantidade de iterações depende de uma condição dinâmica, como ler linhas de um arquivo ou processar input do usuário. until funciona identicamente, mas inverte a lógica: continua repetindo enquanto a condição for falsa, parando quando se torna verdadeira.

#!/bin/bash

# While simples
contador=1
while [[ $contador -le 5 ]]; do
    echo "Contador: $contador"
    contador=$((contador + 1))
done

# While lendo arquivo linha por linha
while IFS= read -r linha; do
    echo "Linha: $linha"
done < /etc/hostname

# While com entrada do usuário
echo "Digite 'sair' para encerrar:"
while [[ true ]]; do
    read -p "Comando: " comando
    if [[ $comando == "sair" ]]; then
        break
    fi
    echo "Você digitou: $comando"
done

# Until (oposto do while)
numero=1
until [[ $numero -gt 3 ]]; do
    echo "Número: $numero"
    numero=$((numero + 1))
done

# While com continue
for i in {1..10}; do
    if [[ $((i % 2)) -eq 0 ]]; then
        continue  # Pula números pares
    fi
    echo "Número ímpar: $i"
done

Break e Continue em Contexto

break sai imediatamente do laço mais próximo, enquanto break 2 sai de dois laços aninhados. continue pula para a próxima iteração do laço atual. Essas construções evitam acúmulo de lógica condicional e tornam o fluxo mais claro.

#!/bin/bash

# Break com laços aninhados
for i in {1..3}; do
    for j in {1..3}; do
        if [[ $j -eq 2 ]]; then
            break 2  # Sai dos dois laços
        fi
        echo "i=$i, j=$j"
    done
done

# Continue prático: processar apenas linhas válidas
while IFS= read -r linha; do
    # Ignora linhas vazias ou com comentários
    [[ -z $linha ]] && continue
    [[ $linha =~ ^# ]] && continue
    echo "Processando: $linha"
done < config.txt

Funções: Reutilizando Código

Funções são blocos de código nomeados que podem ser definidos uma vez e chamados múltiplas vezes. Elas aceitam argumentos (acessíveis via $1, $2, etc., assim como no script principal) e podem retornar um valor usando return. Funções melhoram legibilidade, reduzem duplicação de código e facilitam testes e manutenção.

Em Bash, existem duas sintaxes para definir funções. A primeira, function nome { }, é mais verbosa. A segunda, nome() { }, é mais concisa e preferida na comunidade. Ambas funcionam identicamente. Importante: a função deve ser definida antes de ser chamada.

Definição, Argumentos e Retorno

Uma função recebe argumentos na chamada e acessa-os através de $1, $2, $#, $@ — exatamente como um script recebe argumentos de linha de comando. O return especifica um código de saída numérico (0 a 255), onde 0 indica sucesso. Para retornar dados ao invés de apenas um código, use echo ou printf dentro da função e capture o output com $(funcao).

#!/bin/bash

# Função simples sem argumentos
saudacao() {
    echo "Olá, bem-vindo!"
}

saudacao

# Função com argumentos
saudar_pessoa() {
    local nome=$1
    local sobrenome=$2
    echo "Bem-vindo, $nome $sobrenome!"
}

saudar_pessoa "João" "Silva"

# Função que retorna um valor
calcular_dobro() {
    local numero=$1
    echo $((numero * 2))
}

resultado=$(calcular_dobro 5)
echo "Dobro de 5 é: $resultado"

# Função que retorna código de status
arquivo_existe() {
    local caminho=$1
    if [[ -f $caminho ]]; then
        return 0  # Sucesso
    else
        return 1  # Falha
    fi
}

if arquivo_existe "/etc/passwd"; then
    echo "Arquivo existe"
else
    echo "Arquivo não existe"
fi

Variáveis Locais e Escopo

Por padrão, variáveis em Bash são globais — acessíveis em qualquer lugar do script. Dentro de funções, declare variáveis com local para limitar seu escopo apenas à função. Isso evita conflitos acidentais com variáveis do mesmo nome em outras partes do código e torna a função mais autossuficiente e reutilizável.

#!/bin/bash

contador_global=0

incrementar() {
    local contador_local=10
    contador_global=$((contador_global + 1))
    echo "Local: $contador_local, Global: $contador_global"
}

incrementar
incrementar
echo "Valor global fora da função: $contador_global"

# Modificando variáveis globais dentro da função
valor_importante="original"

modificar() {
    valor_importante="modificado"
}

echo "Antes: $valor_importante"
modificar
echo "Depois: $valor_importante"

Funções com Múltiplos Argumentos e Validação

Funções bem construídas validam seus argumentos e tratam erros apropriadamente. Uma prática comum é verificar se o número correto de argumentos foi passado e exibir uma mensagem de uso se houver problema.

#!/bin/bash

# Função com validação
copiar_arquivo() {
    if [[ $# -ne 2 ]]; then
        echo "Uso: copiar_arquivo <origem> <destino>"
        return 1
    fi

    local origem=$1
    local destino=$2

    if [[ ! -f $origem ]]; then
        echo "Erro: arquivo de origem não existe"
        return 1
    fi

    cp "$origem" "$destino"
    echo "Arquivo copiado com sucesso"
}

# Função que processa variadic arguments
somar_numeros() {
    if [[ $# -eq 0 ]]; then
        echo "Nenhum número fornecido"
        return 1
    fi

    local soma=0
    for numero in "$@"; do
        soma=$((soma + numero))
    done
    echo $soma
}

resultado=$(somar_numeros 10 20 30 40)
echo "Soma: $resultado"

# Função que itera sobre argumentos
listar_argumentos() {
    local contador=1
    for arg in "$@"; do
        echo "Argumento $contador: $arg"
        contador=$((contador + 1))
    done
}

listar_argumentos "primeiro" "segundo" "terceiro"

Conclusão

Shell scripting com Bash é uma habilidade essencial para qualquer profissional de tecnologia. Os quatro pilares abordados — variáveis, condicionais, laços e funções — formam a base para automatizar tarefas, processar dados e construir ferramentas poderosas diretamente no terminal. Dominar esses conceitos permite escrever scripts que são não apenas funcionais, mas também legíveis e fáceis de manter.

A prática deliberada é crucial: comece com scripts simples que combinam uma ou duas dessas estruturas, depois avance para casos mais complexos. Leia exemplos de scripts reais (especialmente em repositórios como /usr/local/bin ou projetos open source) para internalizar padrões idiomáticos do Bash. Finalmente, sempre teste seus scripts e trate erros explicitamente — um bom script não apenas faz o que deveria fazer, mas também falha de forma previsível e informativa quando algo dá errado.

Referências


Artigos relacionados