Tutorial de Shell Script para Iniciantes - Variáveis, If e Loops no Bash

Tutorial de Shell Script para Iniciantes - Variáveis, If e Loops no Bash

O que você vai conseguir fazer

  • Criar e executar seus próprios scripts com shebang e permissão de execução
  • Automatizar trabalho combinando variáveis, condicionais e loops
  • Isolar e corrigir as causas dos erros de sintaxe mais comuns

Pré-requisitos (leia estes primeiro)

Um shell script é um arquivo que reúne os comandos que você digitaria no terminal, para que possam ser executados de uma vez. "Shell script", "script bash" e "programa de shell" significam a mesma coisa.

Coloque em um arquivo o trabalho que antes era digitado à mão e, na próxima vez, uma única execução resolve. Esta edição de fundamentos cobre a sintaxe do Bash desde variáveis e condicionais até loops.

O Que Você Vai Aprender

  • Você vai conseguir criar e executar seus próprios scripts, entendendo a linha shebang e a permissão de execução.
  • Você vai conseguir automatizar trabalho repetitivo combinando variáveis, condicionais e loops.
  • Você vai conseguir isolar e corrigir as causas dos erros de sintaxe mais comuns.

Para quem é: quem já digitou comandos no terminal e quer automatizar o trabalho que se repete.

Pré-requisitos: este artigo lê variáveis como $HOME. Se isso for novo para você, leia primeiro Fundamentos de Variáveis de Ambiente.

Termos definidos antes de começar

Cada termo abaixo é definido uma única vez, aqui.

  • Um shell é o programa que recebe os comandos digitados e os executa. No Linux, o Bash é o padrão.
  • Um shebang é a linha #!/bin/bash no topo do script. Também é chamado de hashbang. Ela declara qual shell deve executar o arquivo.
  • A permissão de execução é a permissão que autoriza um arquivo a ser iniciado como programa. Um script sem ela para com Permission denied.
  • Uma variável é uma caixa que guarda um valor sob um nome. name="valor" coloca o valor dentro; $name o retira.
  • Aspas são " e '. Se um valor contém espaços, o shell o lê como palavras separadas caso não esteja entre aspas.
  • O status de saída é o número de 0 a 255 que um comando devolve ao terminar. 0 significa sucesso e qualquer outro valor significa falha. Também é chamado de código de saída ou código de retorno.
  • A substituição de comando é a forma $(date), que embute a saída de um comando no lugar.
  • A expansão aritmética é a forma $((1 + 2)), que embute o resultado de um cálculo. Sem ela, o shell trata números como texto, então cálculos exigem essa forma. Ela lida apenas com inteiros, e a parte fracionária é descartada.

Seu Primeiro Shell Script

Conclusão: #!/bin/bash, chmod +x, ./script.sh -- três passos que iniciam todo script Bash.

Estrutura Básica

#!/bin/bash
# Isso e um comentario

echo "Ola, Shell Script!"
echo "Data/hora atual: $(date)"
echo "Nome de usuario: $USER"

Criando e Executando Scripts

Passo 1: Criar Arquivo

$ nano hello.sh

Digite o código acima e salve.

Passo 2: Adicionar Permissão de Execução

$ chmod +x hello.sh

Passo 3: Executar Script

$ ./hello.sh
Ola, Shell Script!
Data/hora atual: Sat Jan 11 14:30:00 JST 2025
Nome de usuario: user

O formato que o date imprime depende do locale. Em um locale japonês (ja_JP.UTF-8), por exemplo, a saída sai como 2025年 1月11日 土曜日 14時30分00秒 JST.

O ./ em ./hello.sh significa "no diretório em que estou". Sem ele, o shell procura apenas no PATH, o que termina em command not found.

Sobre o Shebang

#!/bin/bash é chamado de shebang e especifica o interpretador, ou seja, o programa responsável por executar o arquivo.

  • #!/bin/bash -- Usar Bash
  • #!/bin/sh -- Usar shell POSIX
  • #!/usr/bin/env bash -- Auto-detectar do ambiente

Se o shebang for esquecido, o comportamento depende de qual shell acabar executando o arquivo. Com #!/bin/sh, em especial, a sintaxe exclusiva do Bash (como [[ ]] e arrays) não está disponível. Na dúvida, escreva #!/bin/bash.

Variáveis e Entrada

Conclusão: Sem espaços ao redor do =; entre aspas todos os usos; use $() para substituição; read -p para entrada.

Fundamentos de Variáveis

#!/bin/bash

# Definir variaveis (sem espacos ao redor do =)
name="Usuario Linux"
age=25
today=$(date +%Y-%m-%d)

# Usar variaveis
echo "Nome: $name"
echo "Idade: ${age} anos"
echo "Data de hoje: $today"

Exemplo de Cálculo

#!/bin/bash

num1=10
num2=3

sum=$((num1 + num2))
diff=$((num1 - num2))
product=$((num1 * num2))
quotient=$((num1 / num2))

echo "Adicao: $num1 + $num2 = $sum"
echo "Subtracao: $num1 - $num2 = $diff"
echo "Multiplicacao: $num1 x $num2 = $product"
echo "Divisao: $num1 / $num2 = $quotient"
Adicao: 10 + 3 = 13
Subtracao: 10 - 3 = 7
Multiplicacao: 10 x 3 = 30
Divisao: 10 / 3 = 3

O resultado 3 na divisão não é um erro. A expansão aritmética do shell é aritmética de inteiros, e a parte fracionária é descartada ($((10 / 3)) dá 3). Quando precisar de decimais, use bc ou awk.

echo "scale=2; 10/3" | bc      # 3.33
awk 'BEGIN {print 10/3}'       # 3.33333

Variáveis Especiais

Variável Descrição Exemplo
$0 Nome do script ./script.sh
$1, $2, ... Argumentos da linha de comando 1o arg, 2o arg
$# Número de argumentos 3 para 3 argumentos
$@ Todos os argumentos "arg1" "arg2" "arg3"
$? Status de saída do último comando 0 para sucesso, não-0 para falha
$$ ID do processo atual 12345
$USER Nome de usuário atual user
$HOME Diretório home /home/user
$PWD Diretório atual /home/user/scripts

Entrada do Usuário

Entrada Básica

#!/bin/bash

echo "Por favor, digite seu nome:"
read name
echo "Ola, ${name}!"

Entrada com Prompt

#!/bin/bash

read -p "Digite sua idade: " age
read -s -p "Digite a senha: " password
echo
echo "Idade: $age"
echo "Senha digitada silenciosamente"

Múltiplos Valores de Uma Vez

#!/bin/bash

echo "Digite nome e idade (separados por espaco):"
read name age
echo "Nome: $name, Idade: $age"

Condicionais

Conclusão: Espaços dentro de [ ] obrigatórios; variáveis de string entre aspas; use -eq/-gt/-lt para números.

Instrução if

Instrução if Básica

#!/bin/bash

read -p "Digite um numero: " num

if [ "$num" -gt 0 ]; then
    echo "$num e positivo"
elif [ "$num" -lt 0 ]; then
    echo "$num e negativo"
else
    echo "$num e zero"
fi

Verificação de Existência de Arquivo

#!/bin/bash

filename="test.txt"

if [ -f "$filename" ]; then
    echo "$filename existe"
    echo "Tamanho do arquivo: $(wc -c < "$filename") bytes"
else
    echo "$filename nao existe"
    echo "Criando arquivo..."
    touch "$filename"
fi

Operadores Condicionais

Comparação Numérica

Operador Significado Exemplo
-eq Igual [ $a -eq $b ]
-ne Diferente [ $a -ne $b ]
-gt Maior que [ $a -gt $b ]
-ge Maior ou igual [ $a -ge $b ]
-lt Menor que [ $a -lt $b ]
-le Menor ou igual [ $a -le $b ]

Comparação de Strings

Operador Significado Exemplo
= Igual [ "$a" = "$b" ]
!= Diferente [ "$a" != "$b" ]
-z String vazia [ -z "$str" ]
-n Não vazia [ -n "$str" ]

Testes de Arquivo

Operador Significado Exemplo
-f Arquivo regular [ -f file.txt ]
-d Diretório [ -d /home ]
-e Existe [ -e path ]
-r Legível [ -r file ]
-w Gravável [ -w file ]
-x Executável [ -x script ]

Instrução case

#!/bin/bash

echo "Selecione uma opcao:"
echo "1) Listar arquivos"
echo "2) Mostrar hora atual"
echo "3) Mostrar info do sistema"
echo "4) Sair"

read -p "Escolha (1-4): " choice

case $choice in
    1)
        echo "=== Lista de Arquivos ==="
        ls -la
        ;;
    2)
        echo "=== Hora Atual ==="
        date
        ;;
    3)
        echo "=== Info do Sistema ==="
        uname -a
        ;;
    4)
        echo "Saindo."
        exit 0
        ;;
    *)
        echo "Escolha invalida."
        ;;
esac

Loops

Conclusão: {1..N} para intervalos, while IFS= read para linhas de arquivo, until para condições inversas.

Loop for

Loop for Básico

#!/bin/bash

# Intervalo numerico
for i in {1..5}
do
    echo "Contagem: $i"
done

echo "---"

# Processamento de arquivos
for file in *.txt
do
    echo "Processando: $file"
    wc -l "$file"
done

Loop com Arrays

#!/bin/bash

fruits=("apple" "banana" "orange" "grape")

echo "Lista de frutas:"
for fruit in "${fruits[@]}"
do
    echo "- $fruit"
done

Loop for Estilo C

#!/bin/bash

echo "Tabela de multiplicacao (parcial):"
for ((i=1; i<=5; i++))
do
    for ((j=1; j<=5; j++))
    do
        result=$((i * j))
        printf "%2d " $result
    done
    echo
done

Loop while

Loop while Básico

#!/bin/bash

count=1
while [ $count -le 5 ]
do
    echo "Iteracao do loop $count"
    count=$((count + 1))
done

Lendo Arquivos

#!/bin/bash

filename="data.txt"

if [ -f "$filename" ]; then
    while IFS= read -r line
    do
        echo "Lido: $line"
    done < "$filename"
else
    echo "Arquivo $filename nao encontrado"
fi

Loop until

#!/bin/bash

count=1
until [ $count -gt 5 ]
do
    echo "Contagem: $count"
    count=$((count + 1))
done

Controle de Loop

  • break -- Sair do loop
  • continue -- Pular para a próxima iteração
#!/bin/bash

for i in {1..10}
do
    if [ $i -eq 3 ]; then
        echo "Pulando 3"
        continue
    fi

    if [ $i -eq 8 ]; then
        echo "Parando em 8"
        break
    fi

    echo "Numero: $i"
done

Erros Comuns de Iniciantes e Armadilhas

Conclusão: Seis armadilhas em scripts Bash: espacamento, chaves, colchetes, aspas, crases, matemática.

Erro 1: Espaços na Atribuição de Variável

NG (causa erro)

name = "Taro"     # Espaco antes/depois do =
age= 25          # Espaco depois do =
city ="Tokyo"     # Espaco antes do =

Resulta em erro "command not found".

OK (uso correto)

name="Taro"      # Sem espacos ao redor do =
age=25
city="Tokyo"

A atribuição de variável não pode ter espaços ao redor do =.

Erro 2: Chaves Ausentes na Referência de Variável

NG (resultados inesperados)

filename="test"
echo "$filenameback.txt"    # imprime apenas ".txt" ($filenameback e indefinida)
echo "$filename_backup"     # nao imprime nada ($filename_backup e indefinida)

O shell lê $filenameback como um único nome de variável. Ele nunca divide isso em $filename mais back.

OK (uso correto)

filename="test"
echo "${filename}back.txt"   # testback.txt
echo "${filename}_backup"    # test_backup

Use {} para definir claramente o escopo do nome da variável.

Erro 3: Espaços Ausentes nas Condições do if

NG (erro de sintaxe)

if [$num -gt 5]; then       # Sem espaco depois do [
if [ $num -gt 5]; then      # Sem espaco antes do ]

OK (uso correto)

if [ $num -gt 5 ]; then     # Espacos dentro de [ ] obrigatorios
if [[ $num -gt 5 ]]; then   # Mesmo para [[ ]]
if (( num > 5 )); then      # Expressao aritmetica

Erro 4: Aspas Ausentes na Comparação de Strings

NG (exemplos perigosos)

if [ $name = John Doe ]; then    # Problemas com espacos
if [ $empty_var = "" ]; then     # Erro quando vazio

OK (uso seguro)

if [ "$name" = "John Doe" ]; then    # Aspas em ambos os lados
if [ "$empty_var" = "" ]; then       # Sem erro quando vazio
if [ -z "$var" ]; then               # Opcao dedicada para verificar vazio

Erro 5: Sintaxe Antiga de Substituição de Comando

NG (não recomendado)

date=`date`                 # Crases
result=`cat `which ls``     # Aninhamento causa erro

OK (sintaxe moderna)

date=$(date)                # Use $()
result=$(cat $(which ls))   # Facil de aninhar

Erro 6: Erros de Operação Aritmética

NG (não calculado)

result = $num1 + $num2      # Interpretado como comando "result" (command not found)
sum="$a + $b"               # Nao calculado

OK (métodos corretos de cálculo)

result=$((num1 + num2))     # Expansao aritmetica
let "result = num1 + num2"  # Usar comando let

Regras Básicas para Prevenir Erros

Fundamentos de Escrita

  • Atribuição de variável: sem espaços ao redor do =
  • Uso de variável: sempre entre aspas "$var"
  • Variáveis complexas: use chaves "${var}"

Fundamentos de Condicionais

  • Usando [ ]: sempre inclua espaços internos
  • Comparação de strings: aspas em ambos os lados
  • Comparação numérica: use -eq, -gt, -lt, etc.

Depuração e Solução de Problemas

Quando um script não funciona como esperado, não chute: use set -x para exibir o rastro de execução. Ver como cada variável foi expandida, linha por linha, torna a causa fácil de identificar.

set -x: exibir o rastro de execução

#!/bin/bash
set -x              # exibir cada comando a partir daqui

filename="data.txt"
echo "DEBUG: filename = [$filename]"

if [ ! -f "$filename" ]; then
    echo "ERRO: Arquivo $filename nao encontrado" >&2
    exit 1
fi
+ filename=data.txt
+ echo 'DEBUG: filename = [data.txt]'
DEBUG: filename = [data.txt]
+ '[' '!' -f data.txt ']'
+ echo 'ERRO: Arquivo data.txt nao encontrado'
ERRO: Arquivo data.txt nao encontrado
+ exit 1

As linhas que começam com + são os comandos que o shell realmente executou. Como você as vê depois da expansão das variáveis, causas como "a variável estava vazia" ou "as aspas não valeram" ficam identificáveis na hora. O valor é impresso entre [ ] para distinguir uma string vazia de um único espaço.

set -e / set -u: parar problemas cedo

#!/bin/bash
set -e              # parar assim que um comando falhar
set -u              # parar assim que uma variavel indefinida for referenciada

Esses dois existem para impedir que o processamento siga adiante sem que o problema seja notado. O set -u é especialmente eficaz para pegar erros de digitação em nomes de variável.

Em um script com set -u habilitado, referenciar uma variável indefinida interrompe a execução naquela linha com um erro unbound variable. Esse é o comportamento pretendido, não uma falha do set -x.

script.sh: line 5: var: unbound variable

Onde você não quiser que pare, forneça um valor padrão como ${var:-}, que trata a variável indefinida como string vazia.

Mensagens de Erro Comuns e Soluções

Sintoma: command not found

Causa: espaços ao redor do = em uma atribuição de variável, ou execução do script sem ./.

Verificação:

bash -n script.sh    # apenas checagem de sintaxe (nao executa)

Correção: remova os espaços ao redor do =. Chame o script como ./script.sh.

Sintoma: Permission denied

Causa: o script não tem permissão de execução.

Verificação:

ls -l script.sh

A ausência de x em algo como -rw-r--r-- indica que não há permissão de execução.

Correção:

chmod +x script.sh

Sintoma: syntax error near unexpected token

Causa: espaços ausentes dentro de [ ], aspas não fechadas ou then / fi / done ausentes.

Verificação:

bash -n script.sh

Correção: revise as linhas em torno do número informado. O if precisa de then e fi correspondentes; for e while precisam de do e done.

Sintoma: uma variável sai vazia

Causa: caracteres seguem o nome da variável diretamente, e o shell lê tudo como outra variável.

Verificação:

echo "DEBUG: var = [$var]"

Os colchetes permitem distinguir uma string vazia de uma que contém espaços.

Correção: escreva ${var} para delimitar onde o nome da variável termina.

Checklist de Conclusão

  • [ ] Escreveu um shebang (#!/bin/bash) na primeira linha
  • [ ] Adicionou permissão de execução com chmod +x
  • [ ] Não deixou espaços ao redor do = nas atribuições
  • [ ] Usou aspas nas referências de variável, na forma "$var"
  • [ ] Executou dentro de um diretório de prática, sem sudo, para confirmar o comportamento
  • [ ] Usou bash -n e set -x para isolar a causa quando não funcionou

Próximas Leituras