DevOps & CI/CD

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

18 min de leitura

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

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

<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=&quot;Bem-vindo ao Bash&quot;

idade=25

numero_pi=3.14159

Acessando variáveis

echo &quot;Mensagem: $mensagem&quot;

echo &quot;Idade: $idade&quot;

Forma segura com chaves

arquivo=&quot;dados&quot;

echo &quot;Arquivo: ${arquivo}.txt&quot; # dados.txt

Variáveis de ambiente (já existem no sistema)

echo &quot;Usuário atual: $USER&quot;

echo &quot;Diretório home: $HOME&quot;

echo &quot;Shell utilizado: $SHELL&quot;

Comando de substituição (armazena resultado de um comando)

data_atual=$(date +%d/%m/%Y)

echo &quot;Data: $data_atual&quot;

Outro formato (mais antigo, menos preferido)

lista_arquivos=ls -la

echo &quot;$lista_arquivos&quot;</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 &quot;Nome do script: $0&quot;

echo &quot;Número de argumentos: $#&quot;

echo &quot;Primeiro argumento: $1&quot;

echo &quot;Segundo argumento: $2&quot;

Todos os argumentos

echo &quot;Todos os argumentos (\$@): $@&quot;

Valor de saída do comando anterior

resultado=$(echo &quot;Teste&quot;)

echo &quot;Status anterior: $?&quot; # 0 = sucesso, não-zero = erro

Variáveis globais vs locais

variavel_global=&quot;Sou global&quot;

funcao_teste() {

local variavel_local=&quot;Sou local&quot;

echo &quot;$variavel_global&quot; # Funciona

echo &quot;$variavel_local&quot; # Funciona

}

funcao_teste

echo &quot;$variavel_local&quot; # 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 &quot;Você é maior de idade&quot;

else

echo &quot;Você é menor de idade&quot;

fi

Testando strings

nome=&quot;Maria&quot;

if [[ $nome == &quot;Maria&quot; ]]; then

echo &quot;Olá, Maria!&quot;

fi

Testando se uma variável está vazia

entrada=&quot;&quot;

if [[ -z $entrada ]]; then

echo &quot;Entrada está vazia&quot;

fi

Testando existência de arquivo

if [[ -f /etc/passwd ]]; then

echo &quot;Arquivo /etc/passwd existe&quot;

fi

Testando se é um diretório

if [[ -d /home ]]; then

echo &quot;/home é um diretório&quot;

fi

# Múltiplas condições com AND (&amp;&amp;) e OR (||)

if [[ $idade -ge 18 ]] &amp;&amp; [[ $nome == &quot;Maria&quot; ]]; then

echo &quot;Maria é maior de idade&quot;

fi

if [[ $idade -lt 18 ]] || [[ $nome == &quot;João&quot; ]]; then

echo &quot;É menor de idade OU se chama João&quot;

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 &quot;Conceito: A&quot;

elif [[ $nota -ge 8 ]]; then

echo &quot;Conceito: B&quot;

elif [[ $nota -ge 7 ]]; then

echo &quot;Conceito: C&quot;

elif [[ $nota -ge 6 ]]; then

echo &quot;Conceito: D&quot;

else

echo &quot;Conceito: F (Reprovado)&quot;

fi

Exemplo nested

idade=25

renda=3000

if [[ $idade -ge 18 ]]; then

if [[ $renda -ge 2000 ]]; then

echo &quot;Aprovado para empréstimo&quot;

else

echo &quot;Renda insuficiente&quot;

fi

else

echo &quot;Maior de idade é requisito obrigatório&quot;

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 &quot;Início da semana de trabalho&quot;

;;

Friday)

echo &quot;Quase fim de semana!&quot;

;;

Saturday|Sunday)

echo &quot;Fim de semana - descanse!&quot;

;;

*)

echo &quot;Dia comum da semana&quot;

;;

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 &quot;Operação desconhecida&quot;

;;

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 &quot;Fruta: $fruta&quot;

done

Iterando sobre arquivos

for arquivo in *.txt; do

if [[ -f $arquivo ]]; then

echo &quot;Processando: $arquivo&quot;

wc -l &quot;$arquivo&quot;

fi

done

Iterando sobre resultado de um comando

for usuario in $(cat /etc/passwd | cut -d: -f1 | head -5); do

echo &quot;Usuário: $usuario&quot;

done

For com range numérico (Bash 4+)

for i in {1..5}; do

echo &quot;Número: $i&quot;

done

For com range numérico e passo

for i in {0..20..5}; do

echo &quot;Valor: $i&quot;

done

For com aritmética (C-style)

for ((i=1; i&lt;=3; i++)); do

echo &quot;Iteração $i&quot;

done

Acessando índices em array

frutas=(&quot;maçã&quot; &quot;banana&quot; &quot;laranja&quot; &quot;uva&quot;)

for ((i=0; i&lt;${#frutas[@]}; i++)); do

echo &quot;$i: ${frutas[$i]}&quot;

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 &quot;Contador: $contador&quot;

contador=$((contador + 1))

done

While lendo arquivo linha por linha

while IFS= read -r linha; do

echo &quot;Linha: $linha&quot;

done &lt; /etc/hostname

While com entrada do usuário

echo &quot;Digite &#039;sair&#039; para encerrar:&quot;

while [[ true ]]; do

read -p &quot;Comando: &quot; comando

if [[ $comando == &quot;sair&quot; ]]; then

break

fi

echo &quot;Você digitou: $comando&quot;

done

Until (oposto do while)

numero=1

until [[ $numero -gt 3 ]]; do

echo &quot;Número: $numero&quot;

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 &quot;Número ímpar: $i&quot;

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 &quot;i=$i, j=$j&quot;

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 ]] &amp;&amp; continue

[[ $linha =~ ^# ]] &amp;&amp; continue

echo &quot;Processando: $linha&quot;

done &lt; 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 &quot;Olá, bem-vindo!&quot;

}

saudacao

Função com argumentos

saudar_pessoa() {

local nome=$1

local sobrenome=$2

echo &quot;Bem-vindo, $nome $sobrenome!&quot;

}

saudar_pessoa &quot;João&quot; &quot;Silva&quot;

Função que retorna um valor

calcular_dobro() {

local numero=$1

echo $((numero * 2))

}

resultado=$(calcular_dobro 5)

echo &quot;Dobro de 5 é: $resultado&quot;

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 &quot;/etc/passwd&quot;; then

echo &quot;Arquivo existe&quot;

else

echo &quot;Arquivo não existe&quot;

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 &quot;Local: $contador_local, Global: $contador_global&quot;

}

incrementar

incrementar

echo &quot;Valor global fora da função: $contador_global&quot;

Modificando variáveis globais dentro da função

valor_importante=&quot;original&quot;

modificar() {

valor_importante=&quot;modificado&quot;

}

echo &quot;Antes: $valor_importante&quot;

modificar

echo &quot;Depois: $valor_importante&quot;</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 &quot;Uso: copiar_arquivo &lt;origem&gt; &lt;destino&gt;&quot;

return 1

fi

local origem=$1

local destino=$2

if [[ ! -f $origem ]]; then

echo &quot;Erro: arquivo de origem não existe&quot;

return 1

fi

cp &quot;$origem&quot; &quot;$destino&quot;

echo &quot;Arquivo copiado com sucesso&quot;

}

Função que processa variadic arguments

somar_numeros() {

if [[ $# -eq 0 ]]; then

echo &quot;Nenhum número fornecido&quot;

return 1

fi

local soma=0

for numero in &quot;$@&quot;; do

soma=$((soma + numero))

done

echo $soma

}

resultado=$(somar_numeros 10 20 30 40)

echo &quot;Soma: $resultado&quot;

Função que itera sobre argumentos

listar_argumentos() {

local contador=1

for arg in &quot;$@&quot;; do

echo &quot;Argumento $contador: $arg&quot;

contador=$((contador + 1))

done

}

listar_argumentos &quot;primeiro&quot; &quot;segundo&quot; &quot;terceiro&quot;</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>&lt;!-- FIM --&gt;</p>

Comentários

Mais em DevOps & CI/CD

Como Usar Docker Fundamentos: Imagens, Containers, Volumes e Redes em Produção
Como Usar Docker Fundamentos: Imagens, Containers, Volumes e Redes em Produção

Docker Fundamentos: Compreendendo o Ecossistema de Containerização Docker é u...

O que Todo Dev Deve Saber sobre Prometheus: Coleta de Métricas, PromQL e Alertmanager
O que Todo Dev Deve Saber sobre Prometheus: Coleta de Métricas, PromQL e Alertmanager

Introdução ao Prometheus: Por que Monitorar? Quando você trabalha com sistema...

Dominando Namespaces, RBAC e Segurança em Clusters Kubernetes em Projetos Reais
Dominando Namespaces, RBAC e Segurança em Clusters Kubernetes em Projetos Reais

Namespaces em Kubernetes: Isolamento Lógico e Segurança Namespaces são um rec...