Linux: Fundamentos de Script Bash

Salve uma sequência de comandos Linux em um arquivo, adicione um pouco de lógica (condições, laços) e você tem um Shell script. É o ponto de partida para automatizar todo trabalho repetitivo.

📋 Pré-requisitos: Você já deve conhecer

1. O Que Você Vai Aprender


2. A História do Deploy Manual ao Deploy em Um Clique

(1) O Problema: Digitar as Mesmas 5 Comandos Todo Dia

Xiaoming precisava rodar esses comandos todo dia para fazer deploy: git pull, npm install, npm run build, pm2 restart app, echo "done". 5 comandos, 3 minutos, todo dia.

(2) Um Script de 8 Linhas

Ele escreveu um deploy.sh:

BASH
#!/bin/bash
set -e
echo "Starting deployment..."

cd /var/www/myapp
git pull origin main
npm install
npm run build
pm2 restart app
echo "✅ Deployment complete!"
💡 Dica: set -e é a primeira linha de defesa para a segurança do script — o script para imediatamente se algum comando retornar código de saída diferente de zero. Sem set -e, o script continua rodando mesmo depois de uma falha, o que pode causar erros em cascata sérios (como reiniciar um serviço com código quebrado).

(3) O Benefício: Deploy em Um Clique

A partir daí, Xiaoming só precisava rodar ./deploy.sh — todas as etapas aconteciam automaticamente, economizando tempo e evitando esquecimentos.


3. Pontos-Chave

(1) Shebang

A primeira linha de um script precisa ser o Shebang — diz ao sistema qual interpretador usar:

TEXT
#!/bin/bash      # Executa com bash
#!/bin/sh        # Executa com sh (modo de compatibilidade)
#!/usr/bin/env python3  # Executa com Python 3
💡 Dica: #!/bin/bash é o Shebang — ele diz ao SO qual interpretador usar para rodar o script. Sem essa linha, o sistema usa o shell atual, o que pode causar problemas de compatibilidade (por exemplo, sh não aceita a sintaxe [[ ]]). Todo script bash precisa começar com #!/bin/bash.

(2) Variáveis

TEXT
# Definir variáveis (sem espaço ao redor do igual)
name="Alice"
age=25

# Usar variáveis (chaves são recomendadas)
echo $name          # Alice
echo ${name}        # Alice (chaves são mais seguras)
echo "Hello, ${name}"

# Regras de nomes de variáveis
# 1. Letras, dígitos, sublinhado
# 2. Não pode começar com dígito
# 3. Sensível a maiúsculas/minúsculas (NAME != name)

Três Tipos de Aspas:

Aspas Exemplo Comportamento
Aspas duplas "Hello $name" Interpreta variáveis e comandos
Aspas simples 'Hello $name' String literal, sem interpretação
Crase/$() `date` ou $(date) Executa o comando e captura a saída

ℹ️ Nota: A diferença essencial entre aspas simples e duplas — dentro das aspas duplas, $variavel e $(comando) são interpretados e substituídos; tudo dentro de aspas simples é literal. Use aspas duplas quando quiser interpolar variáveis, e aspas simples quando precisar manter $ e outros caracteres especiais (por exemplo, em padrões de regex ou em heredocs onde você não quer expansão de variáveis).

TEXT
name="Alice"
echo "Hello $name"     # Hello Alice
echo 'Hello $name'     # Hello $name
echo "Today is $(date)"    # Today is Tue Jul  7 10:23:45 CST 2026

(3) Parâmetros Posicionais

TEXT
./script.sh arg1 arg2 arg3
#           $1    $2    $3

$0       # Nome do próprio script
$1, $2   # 1º, 2º argumento
$#       # Quantidade de argumentos
$@       # Todos os argumentos (como palavras separadas)
$*       # Todos os argumentos (como uma string única)
$?       # Código de saída do comando anterior (0=sucesso, !=0=falha)
$$       # PID do script atual

(4) Lógica Condicional (if / test / [[ ]])

TEXT
# Comando test ou [ ]
if [ "$name" = "Alice" ]; then
    echo "Hello Alice"
fi

# [[ ]] é mais poderoso (específico do bash)
if [[ "$name" == "Alice" ]]; then
    echo "Hello Alice"
fi

# if-elif-else
if [ "$1" -gt 10 ]; then
    echo "Maior que 10"
elif [ "$1" -gt 5 ]; then
    echo "Maior que 5"
else
    echo "5 ou menos"
fi

Testes de Arquivo:

Condição Significado
-f arquivo É um arquivo regular
-d dir É um diretório
-e caminho Existe
-s arquivo Tamanho do arquivo > 0
-r arquivo É legível
-w arquivo É gravável
-x arquivo É executável

Comparação de Strings:

Condição Significado
$a = $b Igual
$a != $b Diferente
-z $a String vazia (tamanho = 0)
-n $a String não vazia

Comparação Numérica:

Condição Significado
$a -eq $b Igual
$a -ne $b Diferente
$a -gt $b Maior que
$a -ge $b Maior ou igual
$a -lt $b Menor que
$a -le $b Menor ou igual

(5) Laços

BASH
# Laço for
for i in 1 2 3 4 5; do
    echo "Number: $i"
done

for file in *.txt; do
    echo "Found file: $file"
done

for i in {1..10}; do
    echo "$i"
done

# Laço while
count=1
while [ $count -le 5 ]; do
    echo "Attempt $count"
    count=$((count + 1))
done

# Lê um arquivo linha por linha
while IFS= read -r line; do
    echo "$line"
done < /etc/hostname

(6) ▶ Exemplo: Script Básico

BASH
#!/bin/bash
# hello.sh - Primeiro script bash
echo "Hello, World!"
echo "Current user: $(whoami)"
echo "Current directory: $(pwd)"
echo "File count: $(ls | wc -l)"

Saída:

TEXT
Hello, World!
Current user: alice
Current directory: /home/alice
File count: 12

(7) ▶ Exemplo: Usando Parâmetros Posicionais

TEXT
#!/bin/bash
# greet.sh - Saúda um usuário informado
if [ $# -eq 0 ]; then
    echo "Usage: $0 <username>"
    exit 1
fi

echo "Hello, $1!"
echo "Welcome to Linux scripting."
echo "Script name: $0"
echo "Argument count: $#"
TEXT
./greet.sh Alice
# Hello, Alice!

(8) ▶ Exemplo: if Condicional

TEXT
#!/bin/bash
# check-file.sh - Verifica o estado de um arquivo

FILE="${1:-test.txt}"

if [ ! -e "$FILE" ]; then
    echo "File '$FILE' does not exist"
    exit 1
fi

if [ -f "$FILE" ]; then
    echo "Type: Regular file"
elif [ -d "$FILE" ]; then
    echo "Type: Directory"
fi

[ -r "$FILE" ] && echo "Readable: ✅" || echo "Readable: ❌"
[ -w "$FILE" ] && echo "Writable: ✅" || echo "Writable: ❌"
[ -x "$FILE" ] && echo "Executable: ✅" || echo "Executable: ❌"

(9) ▶ Exemplo: Operação em Lote com Laço for

TEXT
#!/bin/bash
# rename.sh - Renomeia em lote .txt para .md

count=0
for file in *.txt; do
    if [ -f "$file" ]; then
        mv "$file" "${file%.txt}.md"
        echo "Renamed: $file → ${file%.txt}.md"
        count=$((count + 1))
    fi
done

echo "Total files renamed: $count"

(10) ▶ Exemplo: Laço while e Leitura de Entrada

TEXT
#!/bin/bash
# countdown.sh - Timer de contagem regressiva

if [ $# -ne 1 ]; then
    echo "Usage: $0 <seconds>"
    exit 1
fi

count=$1
while [ $count -gt 0 ]; do
    echo "Countdown: $count"
    count=$((count - 1))
    sleep 1
done

echo "Time's up!"

(11) ▶ Exemplo Abrangente: Script de Processamento em Lote

BASH
#!/bin/bash
# batch-process.sh - Processa em lote os arquivos de um diretório

DIR="${1:-.}"
ACTION="${2:-info}"

if [ ! -d "$DIR" ]; then
    echo "Error: Directory '$DIR' does not exist"
    exit 1
fi

echo "Processing directory: $DIR"
echo "Action: $ACTION"
echo "---"

case "$ACTION" in
    info)
        echo "File statistics:"
        echo "  Total files: $(find "$DIR" -type f | wc -l)"
        echo "  Total directories: $(find "$DIR" -type d | wc -l)"
        echo "  Total size: $(du -sh "$DIR" | cut -f1)"
        ;;
    backup)
        BACKUP="/tmp/backup-$(date +%Y%m%d)"
        mkdir -p "$BACKUP"
        cp -r "$DIR" "$BACKUP/"
        echo "Backup complete: $BACKUP"
        ;;
    clean)
        echo "Cleaning .log files..."
        find "$DIR" -name "*.log" -delete
        echo "Cleaning .tmp files..."
        find "$DIR" -name "*.tmp" -delete
        echo "Cleaning complete"
        ;;
    *)
        echo "Actions: info / backup / clean"
        ;;
esac

❓ Perguntas Frequentes

P: O #!/bin/bash na primeira linha é obrigatório? R: Sim. Ele diz ao kernel qual interpretador usar. Sem ele, o padrão é o shell atual (o que pode causar problemas de compatibilidade).

P: Por que meu script diz command not found? R: Dois motivos: 1) Falta de permissão de execução — rode chmod +x script.sh. 2) O diretório atual não está no PATH — use ./script.sh em vez de script.sh.

P: Qual a diferença entre $@ e $*? R: $@ trata cada argumento como uma "palavra" separada; $* junta todos os argumentos em uma única string. Na maioria dos cenários, recomenda-se $@. Iterar com for arg in "$@"; do é a prática padrão.

P: Qual a diferença entre exit 0 e exit 1? R: exit 0 significa sucesso; exit com valor diferente de zero significa falha (números diferentes indicam tipos diferentes de erro). Outros programas podem verificar $? para saber se o script deu certo.

P: Como ler a entrada do usuário em um script? R: Use o comando read: read -p "Digite seu nome: " nome, depois $nome guarda o que o usuário digitou.


📖 Resumo


📝 Exercícios

  1. Básico (⭐): Escreva um script info.sh que exiba o nome do usuário atual, a data, o diretório atual e a contagem de arquivos
  2. Intermediário (⭐⭐): Escreva um script check-file.sh que receba o nome de um arquivo como argumento, verifique se ele existe e exiba seu tipo. Depois escreva um script backup-txt.sh que faça backup de todos os arquivos .txt do diretório atual em /tmp/txt-backup/
  3. Avançado (⭐⭐⭐): Escreva um script de contagem regressiva que receba os segundos como argumento e conte um por segundo, adicionando tratamento de erro com set -e. Em seguida, use um laço for para criar 10 diretórios de test-1 a test-10, e por fim escreva um script que calcule e ordene os tamanhos desses 10 diretórios
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%