GNU/Linux

Execução de scripts shell, compreensão do processo de inicialização do Linux, gerenciamento de serviços e pacotes em alto e baixo nível, diferenciação entre containers e máquinas virtuais, e implementação prática de containers Docker com automação de scripts.

Por Fábio Linhares • Instituto Infnet

EXERCÍCIO 01
Aplicação Shell
Objetivo: Criar scripts para mover, copiar e registrar operações.

Enunciado:
1. Escreva o script mvl.sh que recebe dois argumentos: origem e destino. Este script move origem para destino e salva em log.txt a data e horário que a operação foi executada.
2. Escreva o script cpl.sh que recebe dois argumentos: origem e destino. Este script copia origem para destino e salva em log.txt a data e horário que a operação foi executada.
3. Crie dois arquivos a.txt e b.txt contendo ‘a’ e ‘b’, respectivamente, e o diretório TESTE. Execute os scripts para copiar a.txt e mover b.txt para TESTE. Exiba o conteúdo de log.txt.

1. Script mvl.sh
hacker@matrix:~$nano mvl.sh
#!/bin/bash mv "$1" "$2" echo "MV: '$1' -> '$2' em $(date)" >> log.txt
2. Script cpl.sh
hacker@matrix:~$nano cpl.sh
#!/bin/bash cp "$1" "$2" echo "CP: '$1' -> '$2' em $(date)" >> log.txt
3. Execução e Verificação
hacker@matrix:~$echo 'a' > a.txt && echo 'b' > b.txt
hacker@matrix:~$mkdir -p TESTE
hacker@matrix:~$chmod +x mvl.sh cpl.sh
hacker@matrix:~$./cpl.sh a.txt TESTE/
hacker@matrix:~$./mvl.sh b.txt TESTE/
hacker@matrix:~$ls TESTE/
a.txt b.txt
hacker@matrix:~$cat log.txt
CP: 'a.txt' -> 'TESTE/' em Tue Sep 16 10:20:30 -03 2025 MV: 'b.txt' -> 'TESTE/' em Tue Sep 16 10:20:35 -03 2025
Alternativa: Scripts Robustos com Validação

Scripts profissionais devem validar seus inputs. Vamos criar uma versão mais robusta que verifica se o número correto de argumentos foi passado e se o arquivo de origem realmente existe.

hacker@matrix:~$nano cpl_robust.sh
#!/bin/bash LOGFILE="log.txt" # Verifica se o número de argumentos é diferente de 2 if [ "$#" -ne 2 ]; then echo "Uso: $0 " exit 1 fi # Verifica se o arquivo de origem não existe if [ ! -e "$1" ]; then echo "Erro: A origem '$1' não existe." exit 1 fi cp "$1" "$2" echo "CP: '$1' -> '$2' em $(date)" >> "$LOGFILE" echo "Cópia de '$1' para '$2' realizada com sucesso."

Explicação:
- if [ "$#" -ne 2 ]: $# contém o número de argumentos. -ne significa "não igual". Se não for 2, o script exibe uma mensagem de uso e sai com código de erro 1.
- if [ ! -e "$1" ]: O operador -e verifica se um arquivo ou diretório existe. O ! nega a condição. Se o arquivo de origem não existir, o script avisa e sai.
- exit 1: É uma convenção em shell script que uma saída com valor 0 significa sucesso, e qualquer outro valor (como 1) significa um erro.

EXERCÍCIO 02
Inicialização Linux
Objetivo: Descrever e explicar cada passo da inicialização do Linux.

Enunciado: Descreva e explique cada passo da inicialização do Linux.

O processo de boot do Linux é uma sequência coreografada de eventos que transforma uma máquina desligada em um sistema operacional funcional. Ele pode ser dividido nas seguintes fases principais:

1. Ativação do Firmware (BIOS/UEFI)
Descrição: Ao ligar o computador, o primeiro código executado reside em um chip na placa-mãe. Este é o firmware, que pode ser o tradicional BIOS (Basic Input/Output System) ou o moderno UEFI (Unified Extensible Firmware Interface).
Explicação: O firmware realiza o POST (Power-On Self-Test), um diagnóstico de hardware que verifica a integridade de componentes críticos como CPU, memória RAM e teclado. Após o POST, o firmware consulta sua configuração para determinar a ordem de boot e localiza o primeiro dispositivo inicializável (HD, SSD, USB). Ele então carrega o primeiro setor deste dispositivo na memória RAM e transfere o controle da execução para ele.
2. Carregador de Boot (Bootloader - GRUB2)
Descrição: O setor de boot contém a primeira parte do Bootloader. Em sistemas Linux modernos, este é quase universalmente o GRUB2 (GRand Unified Bootloader 2).
Explicação: O GRUB2 é carregado em estágios. O primeiro estágio é minúsculo e sua única função é localizar e carregar o restante do GRUB2. Uma vez totalmente carregado, o GRUB2 apresenta um menu de inicialização, permitindo ao usuário escolher entre diferentes sistemas operacionais ou versões do Kernel Linux. Ele lê seu arquivo de configuração (/boot/grub/grub.cfg) para saber onde o Kernel e o initramfs estão localizados no disco.
3. Carregamento do Kernel e Initramfs
Descrição: O GRUB2 carrega dois arquivos essenciais na memória RAM: o Kernel (ex: vmlinuz-...) e o initramfs (Initial RAM File System).
Explicação: O Kernel é o núcleo do sistema operacional; ele gerencia a CPU, a memória e os periféricos. No entanto, o Kernel por si só não consegue acessar todos os tipos de hardware. O initramfs é um sistema de arquivos temporário e mínimo que contém os módulos e drivers necessários para que o Kernel possa montar o sistema de arquivos raiz (/) real. Por exemplo, se o seu / está em um disco LVM ou RAID, os drivers para LVM/RAID estão no initramfs. Após carregar os drivers necessários, o Kernel descarta o initramfs e monta o sistema de arquivos raiz definitivo.
4. Início do Processo `init` (systemd)
Descrição: Uma vez que o sistema de arquivos raiz está montado, o Kernel inicia o primeiro processo do espaço do usuário, que possui o Process ID (PID) 1. Em sistemas legados, era o init do SysV. Em sistemas modernos, é o systemd.
Explicação: O systemd é um gerenciador de sistema e serviços. Ele é o processo "pai" de todos os outros processos no sistema. Sua principal tarefa é orquestrar a inicialização dos serviços (daemons) necessários para o funcionamento do sistema, como rede, login, serviços de sistema, etc. O systemd é projetado para iniciar serviços em paralelo, o que acelera significativamente o tempo de boot em comparação com o SysVinit, que os iniciava sequencialmente.
5. Execução de Targets (Níveis de Execução)
Descrição: O systemd trabalha com o conceito de "targets", que são análogos aos "runlevels" do SysVinit. Um target é um ponto de sincronização que agrupa um conjunto de serviços.
Explicação: O systemd lê o target padrão (geralmente um link simbólico em /etc/systemd/system/default.target) para determinar o estado final que o sistema deve alcançar. Os targets mais comuns são multi-user.target (um sistema multiusuário com rede, mas sem interface gráfica) e graphical.target (que depende do multi-user.target e adiciona os serviços para a interface gráfica e a tela de login). Ao atingir o target, o sistema é considerado totalmente inicializado e pronto para uso.
EXERCÍCIO 03
Gerenciamento de inicialização e gerenciamento de pacotes de alto nível
Objetivo: Gerenciar serviços e pacotes com systemctl e apt.

Enunciado:
1. Desabilite o serviço de gerenciamento de rede na inicialização do sistema, mostre que o serviço foi desabilitado, habilite o serviço e mostre que foi habilitado.
2. Procure no repositório de sua distribuição Linux o servidor Mysql, instale o pacote contendo o servidor Mysql.
3. Habilite o servidor Mysql para inicializar junto com o sistema operacional, reinicie o Linux, mostre que o servidor Mysql inicia junto com o sistema operacional.
4. Inicie o servidor Mysql, mostre que o servidor foi iniciado e pare o servidor Mysql.
5. Desinstale o servidor Mysql e mostre que o servidor foi desinstalado.

# 1. Gerenciando o serviço de rede (NetworkManager)
hacker@matrix:~$sudo systemctl disable NetworkManager.service
Removed /etc/systemd/system/multi-user.target.wants/NetworkManager.service.
hacker@matrix:~$systemctl is-enabled NetworkManager.service
disabled
hacker@matrix:~$sudo systemctl enable NetworkManager.service
Created symlink /etc/systemd/system/multi-user.target.wants/NetworkManager.service → /lib/systemd/system/NetworkManager.service.
hacker@matrix:~$systemctl is-enabled NetworkManager.service
enabled
# 2. Procurando e instalando o MySQL
hacker@matrix:~$apt search mysql-server
mysql-server/jammy-updates 8.0.38-0ubuntu0.22.04.1 amd64 MySQL database server (metapackage depending on the latest version)
hacker@matrix:~$sudo apt install mysql-server -y
Setting up mysql-server (8.0.38-0ubuntu0.22.04.1) ...
# 3. Habilitando e verificando o MySQL após o boot
hacker@matrix:~$sudo systemctl enable mysql.service
hacker@matrix:~$sudo reboot
...após reiniciar...
hacker@matrix:~$systemctl status mysql.service
● mysql.service - MySQL Community Server Loaded: loaded (/lib/systemd/system/mysql.service; enabled; vendor preset: enabled) Active: active (running) since Tue 2025-09-16 10:45:12 -03
# 4. Parando e iniciando o MySQL
hacker@matrix:~$sudo systemctl stop mysql.service
hacker@matrix:~$systemctl status mysql.service | grep Active
Active: inactive (dead) since Tue 2025-09-16 10:46:00 -03
hacker@matrix:~$sudo systemctl start mysql.service
hacker@matrix:~$systemctl status mysql.service | grep Active
Active: active (running) since Tue 2025-09-16 10:46:05 -03
# 5. Desinstalando o MySQL
hacker@matrix:~$sudo apt remove --purge mysql-server -y
Removing mysql-server (8.0.38-0ubuntu0.22.04.1) ... Purging configuration files for mysql-server ...
hacker@matrix:~$dpkg -l | grep mysql-server
(nenhuma saída, confirmando a remoção)
Alternativa: `mask` vs `disable`

O comando disable apenas remove o link simbólico que faz o serviço iniciar no boot. Um administrador ou outro serviço ainda pode iniciá-lo manualmente. Para uma desativação mais forte, use mask.

sudo systemctl mask ssh.service

O comando mask cria um link simbólico de /etc/systemd/system/ssh.service para /dev/null. Isso torna impossível iniciar o serviço de qualquer forma, até que ele seja "desmascarado" com sudo systemctl unmask ssh.service. É uma medida de segurança útil para garantir que um serviço vulnerável ou indesejado nunca seja executado.

EXERCÍCIO 04
Gerenciamento de pacotes de baixo nível
Objetivo: Instalar e remover um pacote .deb manualmente.

Enunciado:
1. Faça o download do pacote .deb do navegador google chrome (procure por “google chrome download” no google) e instale o navegador na sua distribuição.
2. Execute o navegador pela linha de comando e desinstale o navegador.

Enquanto apt é um gerenciador de alto nível que resolve dependências, dpkg é a ferramenta de baixo nível que de fato instala pacotes .deb. Vamos usá-lo para instalar o Google Chrome.

# 1. Baixando e instalando o pacote .deb
hacker@matrix:~$wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
Saving to: ‘google-chrome-stable_current_amd64.deb’... 100%
hacker@matrix:~$sudo dpkg -i google-chrome-stable_current_amd64.deb
Setting up google-chrome-stable (128.0.6613.84-1) ...
# 2. Executando e desinstalando
hacker@matrix:~$google-chrome-stable --no-sandbox &
[1] 12345 (O navegador abre em modo gráfico)
hacker@matrix:~$sudo dpkg -r google-chrome-stable
(Reading database ... 135791 files and directories currently installed.) Removing google-chrome-stable (128.0.6613.84-1) ...
Alternativa: Resolvendo Dependências Quebradas

É muito comum que a instalação com dpkg -i falhe devido a dependências não satisfeitas. O dpkg não sabe como baixar outros pacotes. Quando isso acontece, o sistema de pacotes fica em um estado "quebrado".

O gerenciador de alto nível apt pode consertar isso. O comando a seguir instrui o apt a encontrar e instalar quaisquer dependências que estejam faltando para os pacotes já instalados (ou semi-instalados).

# Comando para corrigir um estado de dependências quebradas
hacker@matrix:~$sudo apt --fix-broken install -y
Reading package lists... Done Building dependency tree... Done Correcting dependencies... Done The following additional packages will be installed: libu2f-udev libvulkan1 ... The following NEW packages will be installed: libu2f-udev libvulkan1 ... 0 upgraded, 2 newly installed, 0 to remove and 0 not upgraded. 1 not fully installed or removed. Need to get 1,234 kB of archives. After this operation, 5,678 kB of additional disk space will be used. ... Setting up google-chrome-stable (128.0.6613.84-1) ...

Após rodar este comando, a instalação do Chrome seria concluída com sucesso.

EXERCÍCIO 05
Containers vs. Máquinas Virtuais
Objetivo: Explicar as diferenças, vantagens e desvantagens.

Enunciado: Explique a diferença entre um container e uma máquina virtual e aponte as vantagens e desvantagens de cada um.

A distinção fundamental entre Máquinas Virtuais (VMs) e Containers reside no nível de abstração que cada tecnologia aplica.

Máquina Virtual: Virtualização de Hardware

Uma VM emula uma máquina física completa. Um software chamado Hypervisor (Tipo 1, como VMware ESXi, roda direto no hardware; ou Tipo 2, como VirtualBox, roda sobre um SO existente) cria e gerencia ambientes de hardware virtualizados (vCPU, vRAM, vDisk). Dentro de cada VM, é necessário instalar um sistema operacional completo (Guest OS), com seu próprio Kernel, bibliotecas e, finalmente, a aplicação. Isso resulta em um isolamento extremamente forte, pois cada VM é uma entidade completamente separada, com seu próprio Kernel.

Estrutura da Stack: Infraestrutura Física → Hypervisor → [Guest OS + Bibliotecas + Aplicação]

Container: Virtualização de Sistema Operacional

Um container não emula hardware. Em vez disso, ele virtualiza o próprio sistema operacional. Todos os containers em um mesmo host compartilham o Kernel do sistema operacional hospedeiro. Um Container Engine (como o Docker) é responsável por criar ambientes de espaço de usuário isolados. Cada container empacota apenas a aplicação e suas dependências (bibliotecas e binários), mas não um Kernel inteiro. Esse compartilhamento do Kernel torna os containers incrivelmente leves e rápidos.

Estrutura da Stack: Infraestrutura Física → Host OS (com seu Kernel) → Container Engine → [Bibliotecas + Aplicação]

Critério de Comparação Máquina Virtual (VM) Container
Nível de Abstração Hardware Sistema Operacional
Isolamento de Segurança Forte (Kernel separado para cada VM) Fraco (Kernel do Host é compartilhado, uma vulnerabilidade no Kernel pode afetar todos os containers)
Tamanho da Unidade Grande (Gigabytes), pois inclui um SO completo. Pequeno (Megabytes), pois inclui apenas a aplicação e suas dependências.
Tempo de Inicialização Lento (Minutos), pois precisa bootar um SO inteiro. Rápido (Segundos ou menos), pois inicia apenas um processo no SO existente.
Consumo de Recursos Alto (RAM e CPU são pré-alocados e reservados). Baixo (Recursos são compartilhados e usados sob demanda).
Portabilidade Moderada (dependente da compatibilidade do Hypervisor). Alta (pode rodar em qualquer sistema com um Container Engine compatível).
Caso de Uso Principal Executar múltiplos sistemas operacionais diferentes em um único host; isolar ambientes multi-tenant; rodar aplicações legadas. Arquitetura de microsserviços; pipelines de CI/CD; empacotar e distribuir aplicações de forma consistente entre ambientes.
EXERCÍCIO 06
Edição e execução de scripts
Objetivo: Criar e executar scripts Python e Shell.

Enunciado: Escreva o arquivo test.py que exibe a mensagem “Teste” na tela e o arquivo test.sh que executa test.py e mostra a data e hora da execução.

# Criando o script Python
hacker@matrix:~$echo 'print("Teste")' > test.py
# Criando o script Shell
hacker@matrix:~$nano test.sh
#!/bin/bash echo "Executando script em: $(date)" python3 test.py
# Dando permissão e executando
hacker@matrix:~$chmod +x test.sh
hacker@matrix:~$./test.sh
Executando script em: Tue Sep 16 11:05:15 -03 2025 Teste
Alternativa: Tornando o Script Python Autônomo

Podemos tornar o script Python diretamente executável, sem precisar chamar python3 explicitamente. Isso é feito adicionando uma linha especial no topo do arquivo chamada "shebang".

# Modificando o script Python
hacker@matrix:~$nano test.py
#!/usr/bin/env python3 print("Teste Autônomo")
# Dando permissão de execução ao script Python
hacker@matrix:~$chmod +x test.py
# Modificando o script Shell para chamar o Python diretamente
hacker@matrix:~$nano test.sh
#!/bin/bash echo "Executando script em: $(date)" ./test.py
# Executando novamente
hacker@matrix:~$./test.sh
Executando script em: Tue Sep 16 11:15:00 -03 2025 Teste Autônomo

Explicação: A linha #!/usr/bin/env python3 diz ao sistema operacional para usar o interpretador python3 (encontrado no ambiente do usuário) para executar este arquivo. Após dar permissão de execução (chmod +x), o arquivo se comporta como qualquer outro programa ou script.

EXERCÍCIO 07
Container Docker
Objetivo: Criar uma imagem Docker, copiar scripts e executá-los.

Enunciado:
1. Escreva um Dockerfile que crie uma imagem do Ubuntu versão 18.04, atualize os repositórios do SO, instale o python3.10, copie os arquivos locais test.py e test.sh do Exercício 6 para a imagem e altere as permissões para que seja possível executar o script test.sh dentro da imagem.
2. Implemente o arquivo test.py para exibir a mensagem “PYTHON” na tela e o arquivo test.sh para exibir a mensagem “SHELL SCRIPT” e executar o arquivo test.py. Execute o container docker a partir da imagem criada e verifique as mensagens exibidas pelos scripts.

1. Atualizando os Scripts
hacker@matrix:~$echo 'print("PYTHON")' > test.py
hacker@matrix:~$nano test.sh
#!/bin/bash echo "SHELL SCRIPT" python3 test.py
2. Criando o Dockerfile
hacker@matrix:~$nano Dockerfile
# Usa a imagem base do Ubuntu 18.04 FROM ubuntu:18.04 # Atualiza repositórios, instala dependências e python 3.10 RUN apt-get update && \ apt-get install -y software-properties-common && \ add-apt-repository ppa:deadsnakes/ppa && \ apt-get install -y python3.10 # Copia os arquivos locais para dentro da imagem COPY test.py . COPY test.sh . # Altera a permissão do script para torná-lo executável RUN chmod +x test.sh # Define o comando a ser executado quando o container iniciar CMD ["./test.sh"]
3. Construindo e Executando o Container
# Construindo a imagem a partir do Dockerfile
hacker@matrix:~$docker build -t minha-imagem-teste .
Successfully built a1b2c3d4e5f6 Successfully tagged minha-imagem-teste:latest
# Executando o container a partir da imagem criada
hacker@matrix:~$docker run --rm minha-imagem-teste
SHELL SCRIPT PYTHON
Alternativa: Otimizando a Imagem com `.dockerignore`

Ao executar docker build, todo o diretório atual (o "contexto de build") é enviado para o daemon do Docker. Se houver arquivos grandes e desnecessários (como logs, outros pacotes, etc.), isso pode tornar o build lento. Podemos evitar isso com um arquivo .dockerignore.

hacker@matrix:~$nano .dockerignore
# Ignorar o log de operações log.txt # Ignorar os próprios scripts de automação mvl.sh cpl.sh # Ignorar pacotes baixados *.deb

Agora, ao executar docker build, os arquivos e padrões listados em .dockerignore não serão enviados ao daemon, resultando em um build mais rápido e eficiente, especialmente em projetos grandes.