<h2>Variáveis em Bash: Fundamentos e Boas Práticas</h2>
<p>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: <code>nome_variavel=valor</code>, sem espaços antes ou depois do sinal de igualdade. Quando você precisa acessar o valor armazenado, utilize o símbolo <code>$</code> precedendo o nome.</p>
<p>A diferença entre usar <code>$variavel</code> e <code>${variavel}</code> é 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 <code>nome=João</code> e escreve <code>echo $nome_completo</code>, Bash procurará por uma variável chamada <code>nome_completo</code>, não <code>nome</code>. Já <code>echo ${nome}_completo</code> retorna corretamente <code>João_completo</code>.</p>
<pre><code class="language-bash">#!/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"</code></pre>
<h3>Variáveis Especiais e Argumentos</h3>
<p>Bash fornece variáveis automáticas que recebem informações sobre o script e seus argumentos. A variável <code>$0</code> contém o nome do script, <code>$1</code>, <code>$2</code>, etc., armazenam o primeiro, segundo argumentos passados na linha de comando, e <code>$#</code> indica a quantidade total de argumentos. A variável <code>$@</code> expande para todos os argumentos, preservando espaços, enquanto <code>$*</code> expande como uma string única.</p>
<pre><code class="language-bash">#!/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</code></pre>
<h2>Condicionais: Tomando Decisões no Script</h2>
<p>Estruturas condicionais permitem que seu script execute diferentes blocos de código baseado em testes lógicos. A estrutura fundamental é o <code>if</code>, 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.</p>
<p>O Bash oferece dois formatos para testes: <code>[ ]</code> (mais portável e tradicional) e <code>[[ ]]</code> (mais moderno e seguro, específico do Bash). A forma <code>[[ ]]</code> trata melhor variáveis vazias e suporta expressões regulares, tornando-a preferível na maioria dos casos. Dentro dos colchetes, usamos operadores como <code>-eq</code> (igual numérico), <code>-ne</code> (não igual numérico), <code>-lt</code> (menor que), <code>-gt</code> (maior que), <code>-z</code> (string vazia) e <code>-n</code> (string não-vazia).</p>
<pre><code class="language-bash">#!/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</code></pre>
<h3>Estruturas if-elif-else e Nested</h3>
<p>Quando você precisa testar múltiplas condições sequenciais, use <code>elif</code> (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.</p>
<pre><code class="language-bash">#!/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</code></pre>
<h3>Case: Alternativa Limpa para Múltiplas Condições</h3>
<p>Quando você precisa comparar uma variável contra vários valores fixos, a estrutura <code>case</code> é mais limpa e legível que múltiplos <code>elif</code>. Cada padrão (<code>pattern</code>) é testado sequencialmente, e o primeiro que combinar tem seu bloco executado. Use <code>;;</code> para encerrar cada caso e <code>*)</code> como padrão padrão (equivalente ao <code>else</code>).</p>
<pre><code class="language-bash">#!/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</code></pre>
<h2>Laços: Iterando sobre Dados</h2>
<p>Laços permitem executar um bloco de código múltiplas vezes. O Bash oferece várias estruturas: <code>for</code>, <code>while</code> e <code>until</code>. O <code>for</code> é ideal quando você conhece previamente quantas iterações serão necessárias ou quando itera sobre uma coleção de itens. O <code>while</code> continua executando enquanto uma condição for verdadeira, sendo útil quando a quantidade de iterações é desconhecida antecipadamente. O <code>until</code> funciona de forma inversa ao <code>while</code>, repetindo enquanto a condição for falsa.</p>
<p>Dentro de um laço, as palavras-chave <code>break</code> e <code>continue</code> oferecem controle fino. <code>break</code> encerra o laço imediatamente, enquanto <code>continue</code> pula para a próxima iteração. Essas construções evitam lógica complexa aninhada e tornam o código mais legível.</p>
<h3>For: Iteração com Coleções</h3>
<p>O <code>for</code> 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 (<code>*.txt</code>), de uma variável contendo múltiplas palavras, ou de um comando que produz várias linhas.</p>
<pre><code class="language-bash">#!/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</code></pre>
<h3>While e Until: Iteração Condicional</h3>
<p><code>while</code> é ú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. <code>until</code> funciona identicamente, mas inverte a lógica: continua repetindo enquanto a condição for falsa, parando quando se torna verdadeira.</p>
<pre><code class="language-bash">#!/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</code></pre>
<h3>Break e Continue em Contexto</h3>
<p><code>break</code> sai imediatamente do laço mais próximo, enquanto <code>break 2</code> sai de dois laços aninhados. <code>continue</code> 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.</p>
<pre><code class="language-bash">#!/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</code></pre>
<h2>Funções: Reutilizando Código</h2>
<p>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 <code>$1</code>, <code>$2</code>, etc., assim como no script principal) e podem retornar um valor usando <code>return</code>. Funções melhoram legibilidade, reduzem duplicação de código e facilitam testes e manutenção.</p>
<p>Em Bash, existem duas sintaxes para definir funções. A primeira, <code>function nome { }</code>, é mais verbosa. A segunda, <code>nome() { }</code>, é mais concisa e preferida na comunidade. Ambas funcionam identicamente. Importante: a função deve ser definida antes de ser chamada.</p>
<h3>Definição, Argumentos e Retorno</h3>
<p>Uma função recebe argumentos na chamada e acessa-os através de <code>$1</code>, <code>$2</code>, <code>$#</code>, <code>$@</code> — exatamente como um script recebe argumentos de linha de comando. O <code>return</code> 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 <code>echo</code> ou <code>printf</code> dentro da função e capture o output com <code>$(funcao)</code>.</p>
<pre><code class="language-bash">#!/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</code></pre>
<h3>Variáveis Locais e Escopo</h3>
<p>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 <code>local</code> 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.</p>
<pre><code class="language-bash">#!/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"</code></pre>
<h3>Funções com Múltiplos Argumentos e Validação</h3>
<p>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.</p>
<pre><code class="language-bash">#!/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"</code></pre>
<h2>Conclusão</h2>
<p>Shell scripting com Bash é uma habilidade essencial para qualquer profissional de tecnologia. Os quatro pilares abordados — <strong>variáveis, condicionais, laços e funções</strong> — 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.</p>
<p>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 <code>/usr/local/bin</code> 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.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://www.gnu.org/software/bash/manual/" target="_blank" rel="noopener noreferrer">Bash Manual Official</a></li>
<li><a href="https://www.shellcheck.net/" target="_blank" rel="noopener noreferrer">ShellCheck - Static Analysis Tool for Shell Scripts</a></li>
<li><a href="https://tldp.org/LDP/abs/html/" target="_blank" rel="noopener noreferrer">Bash Scripting Guide (Linux Documentation Project)</a></li>
<li><a href="https://effective-shell.com/" target="_blank" rel="noopener noreferrer">Effective Bash - Practical Guide</a></li>
<li><a href="http://redsymbol.net/articles/unofficial-bash-strict-mode/" target="_blank" rel="noopener noreferrer">Bash Strict Mode</a></li>
</ul>
<p><!-- FIM --></p>