Existe um momento na vida de todo desenvolvedor em que a interface gráfica acaba. Você entra via SSH num servidor para descobrir por que a API caiu às três da manhã, abre um contêiner Docker para investigar um build quebrado ou recebe acesso a uma máquina na nuvem que mostra apenas um cursor piscando. Nessa hora, quem conhece o terminal resolve o problema em minutos. Quem não conhece fica colando comandos da internet sem saber o que eles fazem, e torcendo para nenhum deles ser um rm -rf no lugar errado.

Este guia existe para você estar no primeiro grupo. Ele começa do zero (o que é Linux, o que é um shell, como os arquivos estão organizados) e vai até o que um profissional usa no dia a dia: permissões, processos, serviços com systemd, rede, SSH, shell script, agendamento de tarefas e dezenas de comandos que já salvaram muitas madrugadas. No final há um cheat sheet completo, organizado por categorias, para você consultar sempre que precisar.

Eu escrevi este material para ser a referência que eu mesmo consulto. Por isso ele é longo, organizado por assunto e cheio de exemplos que você pode copiar, colar e, mais importante, entender.

Por que aprender Linux e o terminal

Você pode passar anos programando sem abrir um terminal Linux. Mas, cedo ou tarde, ele aparece, porque o Linux está por baixo de quase tudo que roda software hoje:

  • Servidores e nuvem. Quando você contrata uma VPS ou cria uma máquina virtual na AWS, no Google Cloud ou na Azure, a opção mais comum é uma distribuição Linux. Servidores web, bancos de dados, filas e caches rodam majoritariamente em Linux.
  • Contêineres. Um contêiner Docker é, na essência, um processo Linux isolado por recursos do próprio kernel (namespaces e cgroups). Imagens como node:22-alpine ou ubuntu são sistemas Linux em miniatura. Até no Windows e no macOS o Docker Desktop roda uma pequena máquina virtual Linux por baixo dos panos.
  • CI/CD. Os pipelines do GitHub Actions, GitLab CI e similares costumam rodar em máquinas Linux, e os passos do pipeline são, quase sempre, comandos de shell.
  • Android. Todo celular Android roda sobre o kernel Linux.
  • Supercomputadores. Desde novembro de 2017, todos os 500 supercomputadores da lista TOP500 rodam Linux.
  • Dispositivos embarcados. Roteadores, smart TVs, Raspberry Pi, centrais multimídia de carros, muitos deles rodam Linux.
  • Windows. Com o WSL, o próprio Windows passou a oferecer um Linux de verdade integrado ao sistema.

E por que o terminal, e não uma interface gráfica? Porque o terminal é:

  • Automatizável: tudo que você digita pode virar um script e rodar sozinho, todo dia, sem erro humano.
  • Componível: pequenos programas se encaixam como peças de Lego. A filosofia Unix, resumida por Doug McIlroy, diz: escreva programas que façam uma coisa e a façam bem, e que trabalhem juntos por meio de texto.
  • Universal e estável: um servidor remoto quase nunca tem interface gráfica, e os comandos que você aprende hoje são praticamente os mesmos de décadas atrás.
  • Rápido: renomear 3 mil arquivos, achar qual processo está segurando a porta 3000 ou descobrir o que encheu o disco leva segundos.

O que é Linux, afinal

A palavra "Linux" é usada para duas coisas diferentes, e entender essa diferença esclarece muita coisa.

Kernel: o coração do sistema

No sentido estrito, Linux é um kernel (núcleo). O kernel é o programa que fica entre o hardware e todo o resto. Ele:

  • gerencia processos: decide qual programa usa a CPU a cada instante;
  • gerencia memória: dá a cada processo o seu próprio espaço de memória e impede que um invada o outro;
  • conversa com o hardware por meio de drivers: disco, placa de rede, USB, placa de vídeo;
  • implementa sistemas de arquivos (ext4, XFS, Btrfs...) e a pilha de rede (TCP/IP);
  • aplica a segurança: usuários, permissões, isolamento.

Programas comuns nunca falam direto com o hardware. Eles pedem ao kernel por meio de chamadas de sistema (system calls, ou syscalls), como open, read, write e execve. Quando você roda ls, o programa pede ao kernel a lista de entradas do diretório; o kernel lê o disco e devolve a resposta.

GNU, Linux e as distribuições

Um kernel sozinho não serve para quase nada: você precisa de um shell para digitar comandos, de utilitários como ls e cp, de uma biblioteca C, de um compilador, de um sistema de inicialização e de um jeito de instalar programas.

Boa parte dessas peças veio do Projeto GNU, anunciado por Richard Stallman em 1983 com o objetivo de criar um sistema operacional livre compatível com Unix (GNU significa "GNU's Not Unix"). Em 1991, o GNU já tinha compilador (GCC), shell (bash), utilitários (coreutils) e muito mais, só faltava um kernel pronto. O Linux preencheu essa lacuna. É por isso que a Free Software Foundation defende o nome GNU/Linux para o sistema completo.

Nem todo sistema com kernel Linux usa as ferramentas GNU, porém. O Android tem sua própria biblioteca C e seus próprios utilitários, e o Alpine Linux usa a biblioteca musl e o BusyBox no lugar da glibc e dos coreutils.

Uma distribuição (ou "distro") é o pacote completo: kernel + utilitários + bibliotecas + gerenciador de pacotes + configurações padrão + instalador + (opcionalmente) um ambiente gráfico, mantidos por uma comunidade ou empresa. Ubuntu, Debian e Fedora são distribuições.

Uma história curta

AnoMarco
1969Ken Thompson e Dennis Ritchie criam o Unix nos Bell Labs, da AT&T.
1973O Unix é reescrito na linguagem C, o que o torna portável para outros computadores.
1983Richard Stallman anuncia o Projeto GNU.
1988Surge o padrão POSIX, que define uma interface comum para sistemas do tipo Unix (shell, utilitários, chamadas de sistema).
1991Linus Torvalds, estudante da Universidade de Helsinque, anuncia em 25 de agosto um kernel "só por hobby". A versão 0.01 sai em setembro.
1992O kernel passa a ser distribuído sob a licença GPL.
1993Nascem Slackware e Debian, duas das distribuições mais antigas ainda ativas.
1994Sai o Linux 1.0.
2004A Canonical lança o Ubuntu, com foco em facilidade de uso.
2005Linus cria o Git para gerenciar o desenvolvimento do kernel.
2008Chega ao mercado o primeiro celular com Android, construído sobre o kernel Linux.
2013O Docker populariza os contêineres, que dependem de recursos do kernel Linux.
2016A Microsoft lança o Windows Subsystem for Linux (WSL).
2017Todos os supercomputadores da lista TOP500 passam a rodar Linux.

Dois detalhes curiosos: Linus queria chamar o projeto de "Freax", mas o administrador do servidor FTP onde o código foi publicado batizou a pasta de linux, e o nome pegou. E o Git, que hoje você usa todos os dias, nasceu da necessidade de gerenciar o código do kernel, se quiser se aprofundar nele, veja o guia completo de Git e GitHub. Já a história do Unix e da linguagem C está no guia de linguagem C.

O padrão POSIX é o motivo pelo qual quase tudo que você aprender aqui também funciona no macOS e nos BSDs: todos seguem, em maior ou menor grau, a mesma especificação.

Distribuições: Ubuntu, Debian, Fedora, Arch e companhia

Existem centenas de distribuições, mas elas se agrupam em poucas famílias. As mais importantes:

DistribuiçãoFamíliaPacotesPerfil
Debianindependenteapt / .debEstável e conservadora; base de dezenas de outras distros; muito usada em servidores.
UbuntuDebianapt / .deb (+ snap)A mais popular em desktops e na nuvem. Versões LTS saem a cada dois anos, em abril dos anos pares (22.04, 24.04...).
Linux MintUbuntuapt / .debAmigável para quem vem do Windows.
Fedoraindependente (patrocinada pela Red Hat)dnf / .rpmSoftware recente; vitrine de novidades que depois chegam ao RHEL.
RHELFedora (via CentOS Stream)dnf / .rpmRed Hat Enterprise Linux: comercial, suporte longo, padrão em muitas empresas.
Rocky Linux / AlmaLinuxcompatíveis com RHELdnf / .rpmAlternativas gratuitas ao RHEL, criadas após o fim do CentOS Linux tradicional.
Arch LinuxindependentepacmanRolling release, minimalista, "faça você mesmo". Famosa pela excelente Arch Wiki.
openSUSEindependentezypper / .rpmLeap (estável) e Tumbleweed (rolling release).
AlpineindependenteapkMinúscula (musl + BusyBox); muito usada como base de imagens Docker.

Dois modelos de lançamento aparecem na tabela:

  • Lançamentos fixos (Debian, Ubuntu, RHEL): uma versão é congelada, testada e recebe só correções de segurança por anos. Previsível e ótimo para servidores. LTS (Long Term Support) é o nome que o Ubuntu dá às versões com suporte estendido.
  • Rolling release (Arch, openSUSE Tumbleweed): não existe "versão"; o sistema é atualizado continuamente e você sempre tem o software mais novo, com um pouco mais de risco de quebra.

Qual escolher? Para servidores, Ubuntu LTS ou Debian são escolhas seguras (documentação abundante), ou a família RHEL, se é o padrão da sua empresa. Para desktop, Ubuntu ou Mint são ótimas portas de entrada. Para aprender como tudo funciona por dentro, Arch é um excelente laboratório depois que você já tiver alguma experiência. Para imagens Docker, Alpine ou as variantes slim do Debian.

A boa notícia: a grande maioria dos comandos deste guia é idêntica em todas as distros. As diferenças ficam no gerenciador de pacotes, em alguns caminhos de configuração e em nomes de serviços, e eu aviso sempre que isso acontecer.

As camadas de um sistema Linux

Uma forma útil de visualizar o sistema é em camadas:

Texto
┌──────────────────────────────────────────────────────────┐
│  Aplicações: navegador, VS Code, Node.js, PostgreSQL...  │
├──────────────────────────────────────────────────────────┤
│  Shell e utilitários: bash, zsh, ls, grep, systemd...    │  espaço do
│  Bibliotecas: glibc (ou musl), OpenSSL, libstdc++...     │  usuário
├─────────────────── chamadas de sistema ──────────────────┤
│  Kernel Linux: processos, memória, drivers,              │  espaço do
│  sistemas de arquivos, rede, segurança                   │  kernel
├──────────────────────────────────────────────────────────┤
│  Hardware: CPU, RAM, disco, placa de rede, USB...        │
└──────────────────────────────────────────────────────────┘

Tudo acima da linha das chamadas de sistema roda no espaço do usuário (user space), com acesso restrito. O kernel roda no espaço do kernel, com acesso total ao hardware. Quando um programa trava no espaço do usuário, o sistema continua de pé; quando o kernel trava (o famoso kernel panic), a máquina inteira para.

Se tiver curiosidade de ver as syscalls acontecendo, instale o strace e rode strace -c ls: ele mostra um resumo de todas as chamadas de sistema que o ls fez.

Terminal, shell e console não são a mesma coisa

Esses três termos costumam ser usados como sinônimos, mas são peças diferentes:

  • Terminal (ou emulador de terminal) é a janela: o programa que desenha o texto na tela e captura o que você digita. Exemplos: GNOME Terminal, Konsole, Windows Terminal, iTerm2, Alacritty, Kitty, WezTerm. O nome vem dos terminais físicos das décadas de 1960 e 1970 (os teletypewriters, origem da sigla TTY).
  • Shell é o interpretador de comandos que roda dentro do terminal. Ele lê a linha que você digitou, expande variáveis e curingas, encontra o programa, pede ao kernel para executá-lo e, quando o programa termina, mostra o prompt de novo. Exemplos: bash, zsh, fish.
  • Console é o terminal "principal" da máquina. No Linux existem os consoles virtuais: em muitas distros, Ctrl+Alt+F3 até F6 levam a telas de login em modo texto, e o ambiente gráfico costuma ficar em F1 ou F2 (varia conforme a distro). É um salva-vidas quando a interface gráfica congela.

Uma analogia: o terminal é o aparelho de telefone; o shell é a pessoa do outro lado da linha que entende o que você pede e faz acontecer. O comando tty mostra qual dispositivo de terminal você está usando (algo como /dev/pts/0).

Bash, zsh, fish e sh

  • sh, o Bourne shell original, de 1979. Hoje, /bin/sh aponta para um shell compatível com POSIX: no Debian e no Ubuntu é o dash (enxuto e rápido); em outras distros, como Fedora e Arch, é o próprio bash em modo de compatibilidade.
  • bash, o Bourne Again SHell, do Projeto GNU (1989). É o shell padrão da maioria das distribuições e o que este guia usa.
  • zsh, padrão no macOS desde o Catalina (2019). Muito popular com o framework Oh My Zsh. Para uso interativo, é quase totalmente compatível com o bash.
  • fish, o friendly interactive shell: autossugestões e cores prontas de fábrica, mas com sintaxe própria, não compatível com POSIX (scripts de bash não rodam nele sem adaptação).

Para descobrir qual shell você está usando:

Bash
echo $SHELL   # o seu shell de login (o padrão da sua conta)
ps -p $$      # o shell que está rodando agora ($$ é o PID do shell atual)

Para trocar o shell padrão da sua conta, use chsh -s /usr/bin/zsh (o caminho precisa estar listado em /etc/shells) e abra uma nova sessão.

Como conseguir um terminal Linux

Você não precisa formatar o computador para aprender Linux. Escolha a opção que combina com o seu sistema:

WSL 2 (Windows Subsystem for Linux) roda um kernel Linux de verdade numa máquina virtual leve, integrada ao Windows. Funciona no Windows 11 e no Windows 10 versão 2004 ou superior (build 19041+).

Abra o PowerShell como administrador e rode:

PowerShell
wsl --install

Reinicie o computador. Por padrão, o comando instala o Ubuntu. Na primeira vez que abrir o Ubuntu, você vai criar um usuário e uma senha do Linux, eles são independentes da sua conta do Windows, e essa senha é a que o sudo vai pedir.

Outros comandos úteis do WSL (rodam no PowerShell):

PowerShell
wsl --list --online        # distribuições disponíveis para instalar
wsl --install -d Debian    # instala uma distribuição específica
wsl --list --verbose       # distribuições instaladas e versão do WSL (1 ou 2)
wsl --shutdown             # desliga todas as instâncias do WSL
wsl --update               # atualiza o WSL

Arquivos do Windows dentro do Linux: os discos do Windows ficam montados em /mnt/c, /mnt/d etc. Sua pasta de usuário está em /mnt/c/Users/SeuNome.

Arquivos do Linux dentro do Windows: no Explorador de Arquivos, acesse \\wsl$\Ubuntu (ou \\wsl.localhost\Ubuntu). De dentro do Linux, explorer.exe . abre a pasta atual no Explorador.

Dica de desempenho: guarde seus projetos dentro do sistema de arquivos do Linux (por exemplo, ~/projetos), e não em /mnt/c. Ferramentas como git e npm ficam muito mais rápidas assim. Com a extensão WSL do VS Code, code . abre o editor já conectado ao Linux.

O Windows Terminal (instalado por padrão no Windows 11) é o melhor lugar para usar o WSL: abas, perfis por distro e boa renderização.

Git Bash, Cygwin e MSYS2 também oferecem bash e utilitários GNU, mas não são Linux: não têm systemd, gerenciador de pacotes do Linux nem o kernel por baixo. Servem para comandos simples; para aprender Linux, use o WSL.

Uma máquina virtual na nuvem via SSH

Qualquer provedor de nuvem (AWS, Google Cloud, Azure, DigitalOcean, Hetzner, Oracle...) permite criar uma máquina Linux em minutos. Depois de criada, você se conecta pelo SSH:

Bash
ssh ubuntu@203.0.113.10                      # usuário@endereço
ssh -i ~/Downloads/minha-chave.pem ubuntu@203.0.113.10   # com arquivo de chave

O nome de usuário depende da imagem: nas imagens Ubuntu da AWS é ubuntu; no Amazon Linux, ec2-user; em muitas VPS, root. Se o SSH reclamar de UNPROTECTED PRIVATE KEY FILE, a chave está com permissões abertas demais: rode chmod 600 minha-chave.pem (ou 400). Tudo isso é detalhado na seção de SSH.

Um contêiner Docker descartável

Se você tem Docker instalado, esta é a forma mais rápida de ter um Ubuntu limpo para experimentar:

Bash
docker run -it --rm ubuntu bash
  • -i mantém a entrada padrão aberta (modo interativo);
  • -t aloca um pseudo-terminal, para o prompt funcionar direito;
  • --rm apaga o contêiner quando você sair;
  • ubuntu é a imagem e bash é o comando que vai rodar dentro dela.

Dentro do contêiner você é root (o prompt termina com #) e a imagem é mínima: comandos como curl, ping e vim não vêm instalados. Instale o que precisar:

Bash
apt update && apt install -y curl iputils-ping vim less

Quer testar no Alpine? Use docker run -it --rm alpine sh, ele não traz bash, só o sh do BusyBox. Para trabalhar com arquivos da sua máquina, monte a pasta atual dentro do contêiner:

Bash
docker run -it --rm -v "$PWD":/trabalho -w /trabalho ubuntu bash

Anatomia de um comando

Antes de decorar comandos, vale entender a gramática. Quase tudo no terminal segue o mesmo formato, e quem entende o formato consegue ler qualquer comando novo.

O prompt

O texto que aparece antes do cursor é o prompt. No Ubuntu, ele tem esta cara:

Texto
gabriel@notebook:~/projetos$ ls -la
│       │        │         │
│       │        │         └─ $ = usuário comum (# = root, o administrador)
│       │        └─ diretório atual (~ é a sua pasta pessoal)
│       └─ nome da máquina (hostname)
└─ seu usuário

O formato varia: o Fedora mostra algo como [gabriel@notebook projetos]$, e o zsh do macOS usa % no lugar do $. Tudo isso é configurável pela variável PS1. O que importa é o último caractere: # significa que você está como root, e aí todo cuidado é pouco, porque o sistema não vai impedir nada.

Comando, opções e argumentos

Bash
ls -l -h /var/log
  • ls é o comando, o programa que vai rodar.
  • -l e -h são opções (ou flags): mudam o comportamento do comando. Aqui, -l pede o formato longo e -h pede tamanhos legíveis (KB, MB).
  • /var/log é o argumento: aquilo sobre o que o comando vai agir.

As opções vêm em dois sabores:

  • Curtas: um hífen e uma letra (-l, -a, -h). Podem ser combinadas: ls -l -a -h é o mesmo que ls -lah.
  • Longas: dois hífens e uma palavra (--all, --human-readable). São mais legíveis, ótimas para scripts.

Algumas opções recebem um valor: head -n 5 arquivo.txt, head -n5 arquivo.txt e head --lines=5 arquivo.txt são equivalentes.

Mais algumas regras que evitam dor de cabeça:

  • Maiúsculas importam: ls -r (ordem reversa) e ls -R (recursivo) são opções diferentes.
  • Espaços separam argumentos. Um nome com espaço precisa de aspas ou barra invertida: cd "Meus Documentos" ou cd Meus\ Documentos.
  • -- encerra as opções. Tudo que vem depois é tratado como argumento, mesmo que comece com hífen: rm -- -arquivo-estranho.txt.
  • Ordem: as ferramentas GNU aceitam opções depois dos argumentos (ls /tmp -l); as versões BSD, como as do macOS, param de ler opções no primeiro argumento. Por hábito, coloque as opções antes.

Nem todo comando é um programa separado. Alguns, como cd, echo, export e alias, são builtins, fazem parte do próprio shell. Isso importa na hora de pedir ajuda, como você vai ver agora.

Como pedir ajuda: man, --help, tldr e companhia

Ninguém decora todas as opções de todos os comandos. O que os profissionais sabem é onde procurar.

man, o manual. Todo comando sério tem uma página de manual:

Bash
man ls          # manual do ls
man 5 crontab   # seção 5: o FORMATO do arquivo crontab
man 1 crontab   # seção 1: o COMANDO crontab
man man         # o manual do próprio man

O manual é dividido em seções numeradas. As que você mais vai usar:

SeçãoConteúdoExemplo
1Comandos do usuárioman 1 ls
2Chamadas de sistema do kernelman 2 open
3Funções de bibliotecas (C)man 3 printf
5Formatos de arquivos de configuraçãoman 5 passwd
7Visões gerais e convençõesman 7 signal
8Comandos de administração do sistemaman 8 mount

Na seção SYNOPSIS (sinopse), a notação é sempre a mesma: colchetes indicam algo opcional, reticências indicam que pode repetir. ls [OPTION]... [FILE]... significa "ls, zero ou mais opções, zero ou mais arquivos".

O man abre as páginas no paginador less, então as teclas são as mesmas:

TeclaO que faz
Espaço ou fAvança uma página
bVolta uma página
↑ ↓ ou j kRola linha a linha
/palavraProcura "palavra" para frente
n / NPróxima / anterior ocorrência da busca
g / GVai para o início / fim
hAjuda com todas as teclas
qSai

Dica: no manual do ls, digite /-h e Enter para pular direto para a explicação da opção -h.

--help, o resumo rápido. Quase todo programa GNU aceita --help e imprime um resumo das opções direto no terminal:

Bash
ls --help
ls --help | less    # se for longo demais
grep --help | grep -- "-v"   # procura a explicação de uma opção específica

help, para os builtins do bash. Como cd faz parte do shell, man cd pode nem existir. Use help cd, help export, help for.

tldr, exemplos práticos. O projeto tldr pages resume cada comando nos exemplos mais comuns. É o "me mostra logo como usa":

Bash
sudo apt install tldr   # ou: npm install -g tldr
tldr tar                # na primeira vez, pode ser preciso rodar tldr --update

Procurar comandos pelo assunto:

Bash
apropos compress   # procura "compress" nas descrições do manual (o mesmo que man -k)
whatis ls          # descrição de uma linha do comando

Descobrir o que um nome realmente é:

Bash
type ls        # ls is aliased to `ls --color=auto'  (ou: ls is /usr/bin/ls)
type cd        # cd is a shell builtin
which python3  # /usr/bin/python3: o caminho do executável que vai rodar

E quando você encontrar um comando enorme e misterioso na internet, cole-o no explainshell.com: ele explica cada pedaço usando as próprias páginas de manual.

O sistema de arquivos do Linux

Se você vem do Windows, esqueça as letras de unidade (C:, D:). No Linux existe uma única árvore de diretórios, que começa na raiz, representada por uma barra: /. Outros discos, pendrives e partições não ganham letras, eles são montados em alguma pasta dessa árvore.

A árvore de diretórios a partir da raiz

A organização segue o FHS (Filesystem Hierarchy Standard), um padrão que praticamente todas as distros respeitam. Por isso, depois que você aprende onde as coisas ficam, sabe navegar em qualquer servidor Linux:

Texto
/
├── bin -> usr/bin     programas essenciais (ls, cp, cat...)
├── boot               kernel, initramfs e arquivos do bootloader (GRUB)
├── dev                arquivos de dispositivo (discos, terminais, /dev/null)
├── etc                configurações do sistema, em texto puro
├── home               pastas pessoais dos usuários (/home/gabriel)
├── lib -> usr/lib     bibliotecas compartilhadas e módulos do kernel
├── media              montagem automática de mídias removíveis (pendrives)
├── mnt                ponto de montagem temporário, manual
├── opt                softwares opcionais de terceiros
├── proc               sistema de arquivos virtual: processos e kernel
├── root               pasta pessoal do usuário root
├── run                dados voláteis desde o boot (PIDs, sockets)
├── sbin -> usr/sbin   programas de administração (fdisk, mkfs...)
├── srv                dados servidos pela máquina (sites, FTP)
├── sys                sistema de arquivos virtual: dispositivos e drivers
├── tmp                arquivos temporários (podem sumir no reboot)
├── usr                programas, bibliotecas e dados somente leitura
│   ├── bin            a maioria dos comandos
│   ├── lib            bibliotecas
│   ├── local          software instalado manualmente pelo administrador
│   └── share          dados independentes de arquitetura (docs, ícones, man)
└── var                dados que mudam: logs, filas, caches, bancos
    ├── cache          caches de programas (ex.: pacotes baixados)
    ├── lib            estado de programas (ex.: /var/lib/docker)
    ├── log            logs do sistema e dos serviços
    └── www            convenção para sites no Debian/Ubuntu

Os que você mais vai visitar:

  • /etc, toda a configuração do sistema: /etc/hosts, /etc/fstab, /etc/ssh/sshd_config, /etc/nginx/. Tudo em texto puro, editável com qualquer editor. Faça backup antes de mexer.
  • /var/log, logs: syslog (Debian/Ubuntu) ou messages (família Red Hat), auth.log, nginx/... É o primeiro lugar a olhar quando algo dá errado (junto com o journalctl, que você vai ver adiante).
  • /home, os seus arquivos. /home/gabriel é a "pasta do usuário" do Gabriel. O root tem a própria pasta, /root.
  • /tmp, área temporária que qualquer usuário pode usar. Não guarde nada importante: em muitas distros ela é limpa a cada boot.
  • /usr/local e /opt, onde vai o software que você instala por fora do gerenciador de pacotes. Assim ele não se mistura com o que a distro controla.
  • /proc e /sys, não existem no disco. São janelas que o kernel abre para você inspecionar (e ajustar) o sistema em tempo real.

Repare nas setas em bin, lib e sbin: em muitas distros modernas (Debian, Ubuntu, Fedora, Arch), esses diretórios são links simbólicos para seus equivalentes dentro de /usr, a chamada unificação do /usr (usr merge). Em sistemas mais antigos, eles ainda são diretórios de verdade. Para você, na prática, tanto faz: /bin/ls e /usr/bin/ls funcionam.

Tudo é arquivo

Uma das ideias mais poderosas do Unix: quase tudo é representado como arquivo. Documentos e diretórios, claro, mas também discos, terminais, informações de processos e até o "lixo":

Bash
cat /proc/cpuinfo            # informações da CPU
cat /proc/meminfo            # detalhes da memória
cat /proc/loadavg            # carga do sistema
ls /proc/1/                  # tudo sobre o processo de PID 1
echo "some daqui" > /dev/null  # /dev/null descarta tudo que recebe
head -c 16 /dev/urandom | base64   # 16 bytes aleatórios, em base64

Os dispositivos ficam em /dev: /dev/sda e /dev/nvme0n1 são discos, /dev/sda1 é a primeira partição do primeiro disco SATA/USB, /dev/tty é o seu terminal, /dev/zero produz zeros infinitos.

A consequência prática é enorme: as mesmas ferramentas (cat, grep, redirecionamento) funcionam com documentos, com dispositivos e com informações do kernel.

Caminhos absolutos e relativos

Um caminho (path) diz onde um arquivo está na árvore.

  • Absoluto: começa com / e parte da raiz. Funciona de qualquer lugar: /home/gabriel/projetos/app/package.json.
  • Relativo: parte do diretório atual. Se você está em /home/gabriel, então projetos/app aponta para /home/gabriel/projetos/app.

Alguns atalhos valem em qualquer caminho:

SímboloSignificadoExemplo
/A raiz do sistemacd /
~Sua pasta pessoalcd ~/Downloads
~mariaA pasta pessoal da usuária marials ~maria
.O diretório atual./script.sh
..O diretório pai (um nível acima)cd ../outra-pasta
-O diretório anterior (só no cd)cd -

Arquivos ocultos, maiúsculas e espaços

  • Ocultos: todo arquivo cujo nome começa com ponto é oculto: .bashrc, .gitconfig, .ssh/, .config/. Os famosos dotfiles guardam as configurações dos seus programas. Use ls -a para vê-los. Oculto é só uma convenção de exibição, não uma proteção.
  • Maiúsculas e minúsculas são diferentes: Relatorio.pdf, relatorio.pdf e RELATORIO.pdf podem existir lado a lado na mesma pasta. O Windows (NTFS) e o macOS (APFS, na configuração padrão) não diferenciam, e isso gera um bug clássico: o import Header from './header' funciona no seu Mac, mas quebra no CI em Linux, porque o arquivo se chama Header.tsx.
  • Espaços e acentos são permitidos, mas dão trabalho no terminal. Para arquivos que você vai manipular por linha de comando, prefira nomes como relatorio-final.pdf ou relatorio_final.pdf.
  • Extensões são só convenção. O Linux não precisa de .exe para executar nada; o que torna um arquivo executável é a permissão. O comando file descobre o tipo real olhando o conteúdo.
  • Os únicos caracteres proibidos num nome de arquivo são a barra (/) e o byte nulo.

Com o mapa na cabeça, vamos andar pela árvore.

pwd e cd

pwd (print working directory) mostra onde você está. cd (change directory) muda de diretório:

Bash
pwd                      # /home/gabriel
cd /var/log              # caminho absoluto
cd ..                    # sobe um nível: agora está em /var
cd log                   # caminho relativo: de volta a /var/log
cd                       # sem argumento: volta para a sua home
cd ~/projetos            # ~ é atalho para a home
cd -                     # volta para o diretório anterior (e imprime qual é)
cd "Área de Trabalho"    # nomes com espaço precisam de aspas

O cd - é um dos atalhos mais subestimados: ele alterna entre os dois últimos diretórios, como o Alt+Tab das pastas.

Se você trabalha com links simbólicos, pwd -P mostra o caminho físico real, com os links resolvidos. E se precisar alternar entre vários diretórios, pushd /algum/lugar empilha o diretório atual e entra no novo; popd volta para o último empilhado.

ls em detalhes

ls lista o conteúdo de um diretório. Sozinho, mostra só os nomes; com opções, vira uma ferramenta de investigação:

OpçãoO que faz
-lFormato longo: permissões, dono, tamanho, data
-aMostra ocultos (inclusive . e ..)
-AMostra ocultos, sem . e ..
-hTamanhos legíveis (4.0K, 12M, 1.1G): use com -l
-tOrdena por data de modificação, mais recente primeiro
-rInverte a ordem
-SOrdena por tamanho, maior primeiro
-RLista recursivamente, entrando nas subpastas
-dLista o próprio diretório, não o conteúdo dele
-1Um item por linha (ótimo para scripts)
-iMostra o número do inode
-FAdiciona um indicador de tipo: / pasta, * executável, @ link

As combinações mais úteis no dia a dia:

Bash
ls -lah            # tudo, em formato longo, com tamanhos legíveis
ls -ltr            # por data, o mais recente no FINAL (ótimo para pastas de logs)
ls -lhS            # por tamanho, o maior primeiro
ls -d */           # só os diretórios
ls -l /etc/*.conf  # só os .conf de /etc

A saída de ls -l tem muita informação:

Texto
drwxr-xr-x 2 gabriel gabriel 4096 Sep 23 10:15 docs
-rw-r--r-- 1 gabriel gabriel  220 Sep 23 10:12 notas.txt
-rwxr-xr-x 1 gabriel gabriel  512 Sep 23 10:14 deploy.sh
lrwxrwxrwx 1 gabriel gabriel    9 Sep 23 10:16 atalho -> notas.txt

Cada coluna (tipo e permissões, número de links, dono, grupo, tamanho em bytes, data de modificação e nome) é explicada em detalhes na seção de permissões. Por ora, repare no primeiro caractere: d é diretório, - é arquivo comum e l é link simbólico.

No Ubuntu, o .bashrc padrão já cria alguns atalhos: ll equivale a ls -alF e la a ls -A. As cores (pastas em azul, executáveis em verde, links em ciano, na configuração mais comum) vêm do ls --color=auto.

tree

tree desenha a árvore de diretórios. Não vem instalado em muitas distros:

Bash
sudo apt install tree          # Debian/Ubuntu (dnf/pacman nas outras)
tree                           # a árvore inteira a partir daqui
tree -L 2                      # só dois níveis de profundidade
tree -a                        # inclui arquivos ocultos
tree -d                        # só diretórios
tree -I 'node_modules|.git'    # ignora esses padrões
Texto
.
├── package.json
├── public
│   └── favicon.ico
└── src
    ├── app.ts
    └── utils
        └── format.ts
 
4 directories, 4 files

Tab completion

A tecla mais importante do terminal é Tab. Digite o começo de um comando ou caminho e pressione Tab:

  • se só existe uma opção, o shell completa o nome;
  • se existem várias, um segundo Tab lista as possibilidades.

Funciona com comandos, arquivos, pastas, variáveis e, com o pacote bash-completion, instalado por padrão no Ubuntu, até com subcomandos e opções (git che + Tab vira git checkout; systemctl res + Tab vira systemctl restart). Use o Tab sem moderação: você digita menos, erra menos e ainda confirma que o caminho existe.

Curingas (globbing) e expansão de chaves

Curingas (wildcards ou globs) selecionam vários arquivos por padrão de nome:

CuringaSignificadoExemplo
*Qualquer sequência de caracteres (inclusive nenhuma)*.txt
?Exatamente um caractere qualquerfoto-?.jpg
[abc]Um dos caracteres listadosrelatorio-[abc].pdf
[0-9]Um caractere no intervalolog-[0-9].txt
[!abc]Um caractere que NÃO esteja na lista ([^abc] também funciona no bash)[!.]*
**Qualquer profundidade de subpastas (bash com shopt -s globstar; nativo no zsh)**/*.md
Bash
ls *.txt                  # todos os .txt da pasta
ls foto-?.jpg             # foto-1.jpg, foto-a.jpg...
ls log-202[45]*.gz        # logs de 2024 e 2025
rm backup-[0-9][0-9].tar.gz

O ponto mais importante sobre curingas: quem expande é o shell, não o comando. Quando você digita ls *.txt, o bash troca *.txt pela lista de arquivos que casam com o padrão e só então chama o ls, que recebe uma lista de nomes. Três consequências:

  • Você pode pré-visualizar o que um curinga vai pegar com echo: antes de um rm *.log, rode echo *.log.
  • O * não pega arquivos ocultos (os que começam com ponto).
  • Se nada casar, o bash passa o padrão literalmente, e o comando reclama: ls: cannot access '*.xyz': No such file or directory.

É por isso também que, em comandos como find . -name "*.log", o padrão vai entre aspas: você quer que o find receba o padrão, e não que o shell o expanda antes.

A expansão de chaves parece curinga, mas é outra coisa: ela gera texto, exista ou não um arquivo com aquele nome:

Bash
mkdir -p projeto/{src,tests,docs}   # cria as três pastas de uma vez
touch nota{1..5}.md                 # nota1.md ... nota5.md
echo {01..10}                       # 01 02 03 04 05 06 07 08 09 10
echo {a..e}                         # a b c d e
echo {0..20..5}                     # 0 5 10 15 20 (de 5 em 5)
cp nginx.conf{,.bak}                # = cp nginx.conf nginx.conf.bak
mv relatorio.{txt,md}               # = mv relatorio.txt relatorio.md

O cp arquivo{,.bak} é um truque clássico para fazer backup rápido antes de editar uma configuração. Atenção: a expansão de chaves é recurso do bash e do zsh; ela não existe no sh POSIX (o dash do Debian/Ubuntu), então não conte com ela em scripts que começam com #!/bin/sh.

Arquivos e diretórios: criar, copiar, mover e apagar

touch e mkdir

Bash
touch notas.txt                  # cria um arquivo vazio (ou atualiza a data, se já existir)
touch a.txt b.txt c.txt          # vários de uma vez
mkdir fotos                      # cria um diretório
mkdir -p projeto/src/components  # cria toda a cadeia de pastas que faltar
mkdir -pv backup/2026/{01..12}   # 12 pastas de meses, mostrando cada uma criada

O touch foi feito para atualizar a data de modificação de um arquivo; criar arquivos vazios é um efeito colateral que todo mundo usa. Já o -p do mkdir faz duas coisas úteis: cria os diretórios intermediários e não dá erro se a pasta já existir, perfeito para scripts.

cp: copiar

Bash
cp relatorio.pdf backup/            # copia para dentro da pasta backup
cp relatorio.pdf relatorio-v2.pdf   # copia com outro nome
cp -r fotos/ /mnt/hd/               # copia uma pasta inteira (recursivo)
cp -a site/ /srv/site-backup        # cópia fiel: preserva permissões, datas e links
cp -i config.yml /etc/app/          # pergunta antes de sobrescrever
cp -v *.jpg imagens/                # mostra cada arquivo copiado
cp -u *.log arquivo-morto/          # só copia se a origem for mais nova que o destino
cp -a origem/. destino/             # copia o CONTEÚDO de origem (inclusive ocultos)
OpçãoO que faz
-r / -RRecursivo: necessário para copiar diretórios
-aModo arquivo: recursivo + preserva permissões, datas, dono (quando possível) e links simbólicos
-iInterativo: pergunta antes de sobrescrever
-nNunca sobrescreve arquivos existentes
-uSó copia quando a origem é mais nova
-vVerboso: mostra o que está fazendo
-pPreserva modo, dono e datas (sem ser recursivo)

Dois detalhes que pegam muita gente:

  • O cp sobrescreve sem avisar. Se o destino existir, ele é substituído em silêncio. Use -i quando estiver em dúvida.
  • Copiar uma pasta depende de o destino existir. cp -r fotos backup cria backup como cópia de fotos se backup não existir; se já existir, cria backup/fotos. Para copiar só o conteúdo de uma pasta para dentro de outra, use cp -a fotos/. backup/, o /. significa "tudo dentro de fotos, inclusive ocultos".

mv: mover e renomear

No Linux, mover e renomear são a mesma operação:

Bash
mv rascunho.txt final.txt        # renomeia
mv final.txt ~/Documentos/       # move para outra pasta
mv *.log logs/                   # move vários arquivos
mv -i novo.conf app.conf         # pergunta antes de sobrescrever
mv -n foto.jpg fotos/            # nunca sobrescreve
mv projeto-antigo projeto-novo   # renomeia uma pasta (não precisa de -r)

Dentro do mesmo sistema de arquivos, o mv é instantâneo, mesmo para pastas gigantes: ele só troca o nome na tabela do diretório, sem copiar dados. Entre discos diferentes, ele copia e depois apaga a origem.

Para renomear vários arquivos de uma vez, um laço resolve (os laços são explicados na seção de shell script):

Bash
for f in *.jpeg; do mv -- "$f" "${f%.jpeg}.jpg"; done   # troca .jpeg por .jpg

${f%.jpeg} é o nome do arquivo sem o sufixo .jpeg. As aspas garantem que nomes com espaço funcionem.

rm e rmdir: apagar

Bash
rm arquivo.txt          # apaga um arquivo
rm -i *.log             # pergunta um por um
rm -I *.log             # pergunta uma vez só, se forem mais de 3 arquivos (GNU)
rm -r pasta/            # apaga a pasta e todo o seu conteúdo
rm -rf pasta/           # ...sem perguntar nada e sem reclamar de erros
rm -v *.tmp             # mostra o que foi apagado
rmdir pasta-vazia/      # apaga um diretório, mas só se estiver vazio
  • -r (recursivo) entra em cada subpasta e apaga tudo.
  • -f (force) ignora arquivos inexistentes e nunca pergunta, nem para arquivos protegidos contra escrita.

Para entender links, você precisa conhecer o inode. Cada arquivo no disco tem um inode: uma estrutura com um número único (dentro daquele sistema de arquivos) que guarda os metadados, permissões, dono, tamanho, datas, número de links, e onde estão os blocos de dados. O que o inode não guarda é o nome. O nome fica no diretório, que é basicamente uma tabela "nome → número do inode".

Texto
 entradas de diretório           inode 1311234              blocos de dados
┌──────────────────────┐        ┌───────────────────┐      ┌────────────────┐
│ original.txt ────────┼───────►│ permissões, dono, │─────►│ "olá"          │
│ dura.txt ────────────┼───────►│ tamanho, datas,   │      │                │
└──────────────────────┘        │ links: 2          │      └────────────────┘
                                └───────────────────┘
 simbolico.txt ──► contém apenas o texto "original.txt" ──► resolvido pelo nome
  • Hard link é um segundo nome para o mesmo inode. Os dois nomes são igualmente "originais"; o arquivo só é apagado de verdade quando o último nome some (e nenhum processo o mantém aberto). Limitações: só funciona dentro do mesmo sistema de arquivos e não pode apontar para diretórios.
  • Link simbólico (symlink, ou soft link) é um arquivo especial que contém um caminho. Funciona como um atalho: pode apontar para diretórios e para outros discos, mas quebra se o alvo for movido ou apagado.

Veja na prática:

Bash
echo "olá" > original.txt
ln original.txt dura.txt            # hard link
ln -s original.txt simbolico.txt    # link simbólico
ls -li
Texto
1311234 -rw-r--r-- 2 gabriel gabriel  5 Sep 23 10:20 dura.txt
1311234 -rw-r--r-- 2 gabriel gabriel  5 Sep 23 10:20 original.txt
1311240 lrwxrwxrwx 1 gabriel gabriel 12 Sep 23 10:20 simbolico.txt -> original.txt

Repare: original.txt e dura.txt têm o mesmo número de inode e contador de links igual a 2. O link simbólico tem outro inode, e seu "tamanho" (12 bytes) é exatamente o comprimento do texto original.txt. Agora apague o original:

Bash
rm original.txt
cat dura.txt         # olá  (o conteúdo continua lá: ainda existe um nome)
cat simbolico.txt    # cat: simbolico.txt: No such file or directory (link quebrado)

Usos reais de links simbólicos:

Bash
ln -s /etc/nginx/sites-available/meusite /etc/nginx/sites-enabled/   # ativa um site no Nginx (Debian/Ubuntu)
ln -sfn /opt/app/releases/2026-09-23 /opt/app/current   # troca a versão ativa de um deploy
readlink -f /usr/bin/python3                            # mostra para onde o link aponta, resolvido
  • -f substitui o link se ele já existir; -n trata um link existente para diretório como arquivo comum (sem ele, o novo link seria criado dentro do diretório apontado).
  • Um alvo relativo é interpretado a partir da pasta onde o link está, não de onde você está. Na dúvida, use caminhos absolutos.
  • Para remover um link, use rm atalho ou unlink atalho, sem barra no final, para não correr o risco de agir sobre o diretório apontado.

file e stat: o que é e quais são os metadados

file descobre o tipo real de um arquivo pelo conteúdo (os primeiros bytes, chamados de magic numbers), ignorando a extensão:

Texto
$ file foto.jpg script.sh /usr/bin/ls documento
foto.jpg:    JPEG image data, JFIF standard 1.01, ...
script.sh:   Bourne-Again shell script, ASCII text executable
/usr/bin/ls: ELF 64-bit LSB pie executable, x86-64, ...
documento:   PDF document, version 1.7, ...

stat mostra todos os metadados do inode:

Texto
$ stat notas.txt
  File: notas.txt
  Size: 220        Blocks: 8          IO Block: 4096   regular file
Device: 259,2      Inode: 1311234     Links: 1
Access: (0644/-rw-r--r--)  Uid: ( 1000/ gabriel)   Gid: ( 1000/ gabriel)
Access: 2026-09-23 10:12:40.123456789 -0300
Modify: 2026-09-23 10:12:31.987654321 -0300
Change: 2026-09-23 10:12:31.987654321 -0300
 Birth: 2026-09-23 10:12:31.987654321 -0300

As datas confundem muita gente:

  • Access (atime): última leitura do conteúdo. Muitos sistemas atualizam essa data de forma preguiçosa (opção relatime), para economizar escritas.
  • Modify (mtime): última alteração do conteúdo. É a data que o ls -l mostra.
  • Change (ctime): última alteração do inode (permissões, dono, número de links, e também do conteúdo). Não é a data de criação, apesar do "c".
  • Birth: a data de criação, exibida quando o sistema de arquivos e as ferramentas suportam.

Ver e editar arquivos

cat, tac e nl

Bash
cat notas.txt              # mostra o arquivo inteiro
cat -n notas.txt           # com números de linha
cat parte1.txt parte2.txt > completo.txt   # concatena (é daí que vem o nome)
cat -A script.sh           # mostra caracteres invisíveis (GNU)
tac access.log | head      # tac = cat ao contrário: do fim para o começo
nl notas.txt               # numera as linhas não vazias

O cat -A é ótimo para caçar problemas invisíveis: ele mostra $ no fim de cada linha, ^I para tabs e ^M para o carriage return dos arquivos criados no Windows (a causa do famoso erro /bin/bash^M: bad interpreter). No macOS, o equivalente é cat -e.

Use cat para arquivos curtos. Para arquivos grandes, use less. E nunca dê cat num arquivo binário: o terminal pode ficar cheio de lixo e com a configuração bagunçada, se acontecer, digite reset e aperte Enter, mesmo sem ver o que está digitando.

less: o paginador

less abre arquivos de qualquer tamanho instantaneamente, porque não carrega tudo na memória. É o jeito certo de ler logs e arquivos longos:

Bash
less /var/log/syslog
less -N arquivo.txt        # com números de linha
less -S log-largo.log      # não quebra linhas longas (rola para os lados)
less +F /var/log/syslog    # já abre acompanhando o fim do arquivo (como tail -f)
ps aux | less              # pagina a saída de qualquer comando
TeclaO que faz
Espaço / bAvança / volta uma página
j / k ou setasDesce / sobe uma linha
g / GVai para o início / fim do arquivo
/textoProcura "texto" para frente
?textoProcura "texto" para trás
n / NPróxima / anterior ocorrência
&textoMostra só as linhas que contêm "texto" (& e Enter de novo desfaz o filtro)
FModo "follow": acompanha o fim do arquivo; Ctrl+C para sair dele
qSai

O more é o avô do less (daí a piada: less is more). Ele ainda existe, mas o less faz tudo que ele faz e mais.

head e tail

Bash
head arquivo.csv              # as 10 primeiras linhas
head -n 3 arquivo.csv         # as 3 primeiras
head -n -5 arquivo.txt        # tudo, menos as 5 últimas (GNU)
head -c 100 arquivo.bin       # os primeiros 100 bytes
tail arquivo.log              # as 10 últimas linhas
tail -n 50 arquivo.log        # as 50 últimas
tail -n +2 arquivo.csv        # da linha 2 em diante (pula o cabeçalho)

Acompanhando logs em tempo real

Esta é uma das habilidades mais valiosas em produção: ver o log sendo escrito enquanto você reproduz o problema.

Bash
tail -f /var/log/nginx/error.log                  # acompanha as novas linhas
tail -F /var/log/app/app.log                      # idem, e sobrevive à rotação do log
tail -f /var/log/nginx/access.log | grep --line-buffered " 500 "   # só os erros 500
tail -f app.log | grep --line-buffered ERRO | tee erros.txt        # filtra e salva
less +F /var/log/syslog                           # follow dentro do less
journalctl -u nginx -f                            # logs de um serviço systemd
  • -f (follow) fica esperando novas linhas; saia com Ctrl+C.
  • -F é -f com persistência: se o arquivo for rotacionado (renomeado e recriado, como faz o logrotate), o tail reabre o arquivo novo.
  • --line-buffered faz o grep entregar cada linha assim que a encontra; sem isso, quando a saída do grep vai para outro comando, ela pode ficar presa num buffer.
  • A vantagem do less +F: aperte Ctrl+C para parar de acompanhar, navegue e busque com /, e aperte F para voltar a acompanhar.

wc: contar linhas, palavras e bytes

Bash
wc arquivo.txt          #   42  310 2048 arquivo.txt  (linhas, palavras, bytes)
wc -l arquivo.txt       # só as linhas
wc -l < arquivo.txt     # só o número, sem o nome do arquivo
wc -w redacao.txt       # palavras
wc -c imagem.png        # bytes
wc -m texto.txt         # caracteres (considera acentos em UTF-8)
ls | wc -l              # quantos itens há na pasta

nano: o editor amigável

O nano é o editor mais simples do terminal e vem instalado na maioria das distros. Os atalhos aparecem no rodapé: ^ significa Ctrl e M- significa Alt.

Bash
nano /etc/hosts          # vai reclamar de permissão ao salvar...
sudo nano /etc/hosts     # ...então edite arquivos do sistema com sudo
AtalhoO que faz
Ctrl+OSalva (Write Out); confirme o nome com Enter
Ctrl+XSai (pergunta se quer salvar alterações)
Ctrl+WProcura texto (Where Is)
Ctrl+KRecorta a linha atual
Ctrl+UCola o que foi recortado
Ctrl+GAjuda completa

Uma alternativa mais segura ao sudo nano é o sudoedit /etc/hosts (ou sudo -e): ele copia o arquivo, abre a cópia no seu editor com o seu usuário e só grava de volta, como root, quando você salva.

Kit de sobrevivência do vim

Cedo ou tarde você vai cair no vim (ou no vi): muitas vezes sem querer, num git commit ou num crontab -e. O vim é modal: no modo normal as teclas são comandos; no modo de inserção elas digitam texto. A maioria dos sustos vem de tentar digitar no modo normal.

Tecla / comandoO que faz
iEntra no modo de inserção (agora dá para digitar)
EscVolta ao modo normal
:wSalva
:qSai (se não houver alterações pendentes)
:wq ou :xSalva e sai
:q!Sai descartando as alterações
ddApaga (recorta) a linha atual
yy / pCopia a linha / cola abaixo
u / Ctrl+RDesfaz / refaz
/texto e nProcura "texto" / próxima ocorrência
gg / GVai para o início / fim do arquivo
:set numberMostra os números de linha
:%s/velho/novo/gSubstitui todas as ocorrências no arquivo

A regra de ouro para sair do vim: aperte Esc (uma ou duas vezes), digite :q! e Enter. Se quiser aprender de verdade, rode vimtutor: é um tutorial interativo de uns 30 minutos que já vem com o vim.

Para escolher qual editor o sistema abre por padrão (no git commit, no crontab -e...), defina a variável EDITOR no seu ~/.bashrc, por exemplo export EDITOR=nano. No Debian e no Ubuntu, sudo update-alternatives --config editor muda o padrão do sistema.

Buscar arquivos e texto

Duas perguntas aparecem o tempo todo: "onde está aquele arquivo?" (find) e "em qual arquivo está aquele texto?" (grep).

find: encontrar arquivos por qualquer critério

O find percorre a árvore de diretórios e testa cada item contra os critérios que você passar. A estrutura é sempre find [onde] [critérios] [ação]:

Bash
find . -name "*.log"                    # pelo nome (sensível a maiúsculas)
find /etc -iname "*nginx*"              # pelo nome, ignorando maiúsculas
find . -type f -name "*.tmp"            # só arquivos comuns (f)
find . -type d -name "build"            # só diretórios (d); l = links simbólicos
find . -maxdepth 1 -type f              # sem entrar em subpastas
find / -type f -size +500M 2>/dev/null  # maiores que 500 MB, ignorando erros de permissão
find . -type f -empty                   # arquivos vazios
find . -mtime -1                        # modificados nas últimas 24 horas
find /var/log -mtime +30                # modificados há mais de 30 dias
find . -mmin -15                        # modificados nos últimos 15 minutos
find /home -user maria                  # que pertencem ao usuário maria
find . -newer referencia.txt            # mais novos que um arquivo de referência
find . -name "*.js" -not -path "*/node_modules/*"          # exclui node_modules
find . \( -name "*.jpg" -o -name "*.png" \) -size +1M      # OU lógico com parênteses
CritérioSignificado
-name / -inameNome (curingas permitidos, sempre entre aspas) / idem, sem diferenciar maiúsculas
-type f, -type d, -type lArquivo comum, diretório, link simbólico
-size +100M, -size -10kMaior que 100 MiB, menor que 10 KiB (unidades: c bytes, k, M, G)
-mtime -7, -mtime +30Modificado há menos de 7 dias / há mais de 30 dias (em blocos de 24 h)
-mmin -60Modificado há menos de 60 minutos
-user, -groupDono ou grupo
-perm 644Permissões exatas
-maxdepth NDesce no máximo N níveis (coloque antes dos outros critérios)
-not ou !, -oNegação e OU; critérios lado a lado já funcionam como E

Um detalhe do -size: o tamanho é arredondado para cima na unidade pedida, então -size -1M só encontra arquivos vazios. Para "menores que 1 MB", use -size -1024k.

Ações dizem o que fazer com cada resultado. O padrão é -print (mostrar o caminho), mas o poder está no -exec:

Bash
find . -name "*.sh" -exec chmod +x {} \;       # roda chmod uma vez para CADA arquivo
find . -name "*.sh" -exec chmod +x {} +        # passa vários arquivos por execução (mais rápido)
find . -type f -name "*.log" -exec ls -lh {} + # lista com detalhes
find . -name "*.bak" -ok rm {} \;              # como -exec, mas pede confirmação a cada um
find . -type d -name node_modules -prune -exec rm -rf {} +   # apaga todos os node_modules

No -exec, {} é substituído pelo caminho encontrado. Terminar com \; executa o comando uma vez por arquivo; terminar com + junta vários arquivos numa única execução, como o xargs. No último exemplo, o -prune impede o find de entrar nas pastas que ele próprio está apagando.

locate: busca instantânea por nome

O locate consulta um banco de dados de nomes de arquivos em vez de percorrer o disco, por isso responde na hora:

Bash
sudo apt install plocate    # Debian/Ubuntu recentes; em algumas distros, o pacote é o mlocate
locate nginx.conf           # todos os caminhos que contêm "nginx.conf"
locate -i readme            # ignorando maiúsculas
sudo updatedb               # atualiza o banco de dados agora

A contrapartida: o banco é atualizado periodicamente (normalmente uma vez por dia), então arquivos criados há pouco podem não aparecer até você rodar sudo updatedb.

grep: encontrar texto dentro de arquivos

grep imprime as linhas que casam com um padrão. É provavelmente o comando que você mais vai usar depois do cd e do ls:

Bash
grep "erro" app.log                         # linhas que contêm "erro"
grep -i "erro" app.log                      # ignora maiúsculas (ERRO, Erro, erro)
grep -n "TODO" main.ts                      # mostra o número da linha
grep -rn "useEffect" src/                   # recursivo, em todos os arquivos de src/
grep -rn "API_URL" src/ --include="*.ts"    # só em arquivos .ts
grep -rn "console.log" src/ --exclude-dir=node_modules
grep -v "^#" nginx.conf                     # linhas que NÃO começam com #
grep -w "port" config.yml                   # só a palavra inteira (não casa "export")
grep -c "404" access.log                    # conta as linhas que casam
grep -l "senha" *.txt                       # só os nomes dos arquivos que casam
grep -o "user_id=[0-9]*" app.log            # imprime só o trecho que casou
grep -A 3 "Exception" app.log               # + 3 linhas depois (After)
grep -B 2 "Exception" app.log               # + 2 linhas antes (Before)
grep -C 5 "Exception" app.log               # + 5 linhas de contexto dos dois lados
grep -E "erro|falha|timeout" app.log        # expressão regular estendida (OU)
grep -F "1.2.3" versoes.txt                 # texto literal: o ponto não é curinga
grep -q "ok" status.txt && echo "tudo certo"  # silencioso: só o código de saída importa
zgrep "erro" app.log.2.gz                   # grep dentro de arquivos .gz
OpçãoO que faz
-iIgnora maiúsculas/minúsculas
-r / -RRecursivo (-R também segue links simbólicos)
-nMostra o número da linha
-vInverte: mostra as linhas que NÃO casam
-wCasa só palavras inteiras
-cConta as linhas que casam
-l / -LLista os arquivos que casam / que não casam
-oMostra só a parte que casou
-A n, -B n, -C nLinhas de contexto depois, antes, ao redor
-EExpressões regulares estendidas
-FTexto fixo, sem regex (mais rápido e sem surpresas)
-qSilencioso, para usar em if e scripts
--include, --exclude-dirFiltra quais arquivos e pastas entram na busca recursiva

Uma combinação que vale decorar para ler arquivos de configuração sem comentários e sem linhas vazias:

Bash
grep -Ev '^(#|$)' /etc/ssh/sshd_config

Expressões regulares em 5 minutos

O grep (e também o sed, o awk e quase todas as linguagens) usa expressões regulares, uma minilinguagem para descrever padrões de texto:

PadrãoCasa comExemplo
.Qualquer caracterec.sa casa "casa", "cosa"
*O item anterior, zero ou mais vezesab*c casa "ac", "abbbc"
+O item anterior, uma ou mais vezes (ERE)[0-9]+
?O item anterior, opcional (ERE)https? casa "http" e "https"
^ / $Início / fim da linha^ERRO, ;$
[abc], [a-z]Um caractere do conjunto ou intervalo[Ee]rro
[^0-9]Um caractere que NÃO está no conjunto[^ ]+
{n,m}Entre n e m repetições (ERE)[0-9]{3,4}
a|bUma alternativa ou outra (ERE)GET|POST
( )Agrupa (ERE)(ab)+
\Escapa um caractere especial\. casa um ponto literal

BRE e ERE: sem opções, o grep usa expressões regulares básicas (BRE), nas quais +, ?, |, {} e () são caracteres comuns. Com -E, ele usa as estendidas (ERE), em que eles têm significado especial. Para não se confundir, use sempre grep -E quando o padrão for além do básico.

Bash
grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}" access.log | sort -u   # endereços IPv4 (aproximado)
grep -E "^[0-9]{4}-[0-9]{2}-[0-9]{2}" app.log                  # linhas que começam com uma data
grep -oE "[[:alnum:]._%+-]+@[[:alnum:].-]+\.[a-z]{2,}" contatos.txt   # e-mails (simplificado)

ripgrep e fd: as alternativas modernas

Duas ferramentas mais novas deixaram buscas em projetos muito mais agradáveis:

  • ripgrep (rg), um grep recursivo por padrão, muito rápido, que respeita o .gitignore e pula arquivos ocultos e binários. Em um projeto JavaScript, isso significa ignorar o node_modules sem você pedir.
  • fd, um find simplificado, com sintaxe mais curta, cores e também respeitando o .gitignore.
Bash
sudo apt install ripgrep fd-find   # no Debian/Ubuntu, o fd se chama fdfind
rg "useState"                      # busca recursiva a partir daqui
rg -i "erro" -g "*.log"            # ignorando maiúsculas, só em arquivos .log
rg -t py "import requests"         # só em arquivos Python
rg -l "TODO"                       # só os nomes dos arquivos
rg --hidden "API_URL"              # inclui arquivos ocultos
fdfind config                      # arquivos com "config" no nome (fd em outras distros)
fdfind -e md                       # por extensão
fdfind -t d src                    # só diretórios

Elas não substituem grep e find, que estão em qualquer servidor,, mas no seu computador de trabalho valem cada segundo da instalação. O VS Code, aliás, usa o ripgrep por baixo na busca global.

which, whereis, type e command -v

Quando o problema é "qual versão deste programa está rodando?":

Bash
which node          # /home/gabriel/.nvm/versions/node/v22.11.0/bin/node
which -a python3    # todos os python3 encontrados no PATH, na ordem
whereis ls          # binário, código-fonte e página de manual
type -a ls          # alias, função, builtin ou arquivo (mostra todos)
command -v docker   # forma POSIX, ideal para scripts: vazio se não existir

É assim que você descobre, por exemplo, que o node que está rodando é o do nvm e não o que você instalou pelo apt.

Redirecionamento, pipes e composição de comandos

Esta é a seção que transforma um usuário de terminal em alguém produtivo. A ideia central do Unix é que programas pequenos se conectam; aqui você aprende os "encanamentos".

stdin, stdout e stderr

Todo processo nasce com três canais abertos, identificados por números chamados descritores de arquivo (file descriptors):

NúmeroNomePara que servePadrão
0stdin (entrada padrão)De onde o programa lêO teclado
1stdout (saída padrão)Resultado normalA tela
2stderr (saída de erros)Mensagens de erro e diagnósticoA tela

Como stdout e stderr vão para a tela, parecem a mesma coisa, mas são canais separados, e isso permite, por exemplo, salvar os resultados num arquivo e continuar vendo os erros na tela.

Operadores de redirecionamento

Bash
ls -l > lista.txt           # stdout para arquivo (SOBRESCREVE)
echo "mais uma" >> lista.txt  # stdout para arquivo (ACRESCENTA no fim)
find / -name "*.conf" 2> erros.txt       # só o stderr para arquivo
find / -name "*.conf" 2> /dev/null       # descarta os erros
comando > saida.log 2>&1    # stdout E stderr para o mesmo arquivo (forma portável)
comando &> saida.log        # o mesmo, forma curta do bash
comando &>> saida.log       # acrescenta os dois (bash 4+)
comando > /dev/null 2>&1    # silencia tudo
wc -l < arquivo.txt         # stdin vindo de um arquivo
mysql meu_banco < dump.sql  # alimenta um programa com um arquivo
OperadorSignificado
>Redireciona stdout, sobrescrevendo
>>Redireciona stdout, acrescentando
2> / 2>>Redireciona stderr
&> / &>>Redireciona stdout e stderr (bash)
2>&1Faz o stderr ir para onde o stdout está indo agora
<Lê o stdin de um arquivo
<<Here-doc: stdin a partir de um bloco de texto
<<<Here-string: stdin a partir de uma string (bash/zsh)
|Pipe: o stdout de um comando vira o stdin do próximo

O > é traiçoeiro: ele esvazia o arquivo antes de o comando rodar. > arquivo.txt sozinho zera um arquivo, e sort dados.txt > dados.txt resulta num arquivo vazio (o shell trunca o arquivo antes de o sort lê-lo). Para ordenar no próprio arquivo, use sort -o dados.txt dados.txt. Se quiser uma rede de proteção, set -o noclobber faz o bash recusar o > sobre arquivos existentes (>| força quando você tiver certeza).

Por que a ordem de 2>&1 importa

2>&1 significa "faça o descritor 2 apontar para o mesmo lugar que o descritor 1 aponta neste momento". Os redirecionamentos são processados da esquerda para a direita, então:

Bash
comando > saida.log 2>&1   # 1º: stdout -> arquivo; 2º: stderr -> onde o stdout está (arquivo). Tudo no arquivo.
comando 2>&1 > saida.log   # 1º: stderr -> onde o stdout está (a tela); 2º: stdout -> arquivo. Erros na tela!

A segunda forma é um erro clássico: os erros continuam aparecendo na tela e o arquivo fica só com a saída normal.

/dev/null: o buraco negro

/dev/null é um arquivo especial que descarta tudo que é escrito nele e, quando lido, está sempre vazio. Serve para calar saídas indesejadas (2>/dev/null) ou para testar se um comando funciona sem se importar com o que ele imprime (if grep -q ... > /dev/null).

Here-doc e here-string

Um here-doc passa um bloco de várias linhas como entrada de um comando, perfeito para criar arquivos de configuração em scripts:

Bash
cat <<EOF > config.env
APP_NAME=minha-api
APP_PORT=3000
CRIADO_EM=$(date +%F)
EOF
 
cat <<'EOF' > saudacao.sh
echo "Olá, $USER! Hoje é $(date +%F)."
EOF
# com 'EOF' entre aspas, $USER e $(date) vão literalmente para o arquivo
# e só serão expandidos quando saudacao.sh for executado

O delimitador (EOF é só convenção) marca o fim do bloco. Sem aspas, variáveis e comandos dentro do bloco são expandidos; com 'EOF' entre aspas, o texto vai literalmente. Com <<-EOF, os tabs no início das linhas são removidos, o que permite indentar o bloco dentro de um script.

Uma here-string passa uma única string como stdin:

Bash
tr 'a-z' 'A-Z' <<< "texto em caixa baixa"   # TEXTO EM CAIXA BAIXA
grep -c "erro" <<< "$saida_do_comando"

Pipes: ligando comandos

O pipe (|) conecta o stdout de um comando ao stdin do próximo. Os dois rodam ao mesmo tempo, e os dados fluem entre eles sem passar pelo disco:

Bash
ls -l | less                               # pagina uma listagem longa
ps aux | grep nginx                        # filtra a lista de processos
cat access.log | wc -l                     # conta linhas (wc -l < access.log é mais direto)
history | grep "docker run"                # procura no histórico
du -h --max-depth=1 | sort -h | tail -5    # as 5 maiores pastas

Por padrão, só o stdout passa pelo pipe; os erros continuam indo para a tela. Para mandar os dois, use comando 2>&1 | less (ou comando |& less no bash 4+).

Em pipelines longos, pense como numa linha de montagem: cada comando faz uma transformação pequena e passa adiante. Você vai ver muitos exemplos na próxima seção.

tee: salvar e mostrar ao mesmo tempo

tee lê o stdin e escreve em arquivo e na tela simultaneamente, como uma conexão em "T" num encanamento:

Bash
./deploy.sh | tee deploy.log               # vê a saída e guarda uma cópia
./deploy.sh 2>&1 | tee -a deploy.log       # inclui erros e acrescenta ao arquivo
echo "192.168.0.50 meuapp.local" | sudo tee -a /etc/hosts   # escreve num arquivo do root

O último exemplo resolve um problema clássico. Isto não funciona:

Bash
sudo echo "192.168.0.50 meuapp.local" >> /etc/hosts   # Permission denied

O sudo vale só para o echo; o redirecionamento >> é feito pelo seu shell, que não tem permissão de escrita em /etc/hosts. Com sudo tee -a, quem abre o arquivo é o tee, que roda como root. (Adicione > /dev/null no fim se não quiser ver a linha repetida na tela.)

xargs: transformar linhas em argumentos

Alguns comandos não leem do stdin: eles esperam argumentos. O rm, por exemplo, não apaga os arquivos cujos nomes você manda por pipe. O xargs faz a ponte:

Bash
find . -name "*.tmp" | xargs rm                        # funciona, mas quebra com espaços nos nomes
find . -name "*.tmp" -print0 | xargs -0 rm -f          # forma segura: separa por byte nulo
cat urls.txt | xargs -n 1 curl -sO                     # um curl por URL
cat servidores.txt | xargs -I {} ssh {} "uptime"       # {} é substituído por cada linha
find . -name "*.png" -print0 | xargs -0 -P 4 -n 1 optipng   # 4 processos em paralelo
  • -0 usa o byte nulo como separador; combine sempre com find -print0 para suportar nomes com espaços e quebras de linha.
  • -n 1 passa um argumento por execução.
  • -I {} define um marcador que é substituído por cada linha (implica uma execução por linha).
  • -P 4 roda até 4 execuções em paralelo.
  • -r (GNU) não executa nada se a entrada estiver vazia.

Substituição de comando e de processo

Substituição de comando, $(comando), executa o comando e coloca a saída dele no lugar:

Bash
echo "Hoje é $(date +%d/%m/%Y)"
echo "Existem $(ls | wc -l) itens aqui"
tar -czf "backup-$(date +%F).tar.gz" projeto/
cd "$(dirname "$(readlink -f "$0")")"   # pode ser aninhado

Você também vai encontrar a forma antiga, com crases (`date`). Ela funciona, mas não aninha bem; prefira $(...).

Substituição de processo, <(comando), é um recurso do bash que faz a saída de um comando parecer um arquivo. É ótima para comparar saídas sem criar arquivos temporários:

Bash
diff <(sort lista1.txt) <(sort lista2.txt)
diff <(ssh servidor1 cat /etc/nginx/nginx.conf) <(ssh servidor2 cat /etc/nginx/nginx.conf)

Encadeando comandos: &&, || e ;

Bash
mkdir build && cd build            # && : só roda o segundo se o primeiro der CERTO
ping -c 1 servidor || echo "fora do ar"   # || : só roda o segundo se o primeiro FALHAR
cd /tmp; ls; pwd                   # ;  : roda um depois do outro, independente do resultado
npm test && npm run build && npm run deploy   # para no primeiro que falhar

Cuidado com a && b || c: não é um if/else. Se a der certo mas b falhar, c também roda. Para lógica condicional de verdade, use if (veja a seção de shell script).

Códigos de saída

Todo comando termina com um código de saída (exit status), um número de 0 a 255. 0 significa sucesso; qualquer outro valor significa algum tipo de falha. É esse número que o &&, o || e o if consultam. A variável $? guarda o código do último comando:

Bash
ls /etc > /dev/null; echo $?          # 0
ls /nao-existe 2> /dev/null; echo $?  # 2
CódigoSignificado comum
0Sucesso
1Erro genérico
2Uso incorreto ou erro grave (o significado exato varia por ferramenta)
126O arquivo existe, mas não pode ser executado (permissão)
127Comando não encontrado
128 + NProcesso terminado pelo sinal N: 130 = Ctrl+C (sinal 2), 137 = kill -9 (sinal 9)

O 137 aparece muito com Docker e Kubernetes: um contêiner que sai com código 137 foi morto com SIGKILL, frequentemente pelo OOM killer, porque estourou o limite de memória.

Processamento de texto: sort, uniq, cut, sed, awk e amigos

No Unix, texto é a interface universal: logs, CSVs, configurações e a saída de quase todos os comandos são linhas de texto. Com meia dúzia de ferramentas você filtra, transforma e resume qualquer coisa, muitas vezes sem precisar abrir uma planilha ou escrever um script em Python.

Os exemplos desta seção usam este arquivo, vendas.csv:

csv
data,produto,categoria,quantidade,preco
2026-09-01,Teclado,perifericos,2,150.00
2026-09-01,Monitor,video,1,899.90
2026-09-02,Mouse,perifericos,5,49.90
2026-09-02,Teclado,perifericos,1,150.00
2026-09-03,Webcam,video,2,239.00
2026-09-03,Mouse,perifericos,3,49.90

sort e uniq

Bash
sort nomes.txt                  # ordem alfabética
sort -r nomes.txt               # ordem reversa
sort -n numeros.txt             # ordem NUMÉRICA (sem -n, "10" vem antes de "9")
sort -h tamanhos.txt            # entende sufixos: 1.5K < 10K < 512M < 2G (GNU)
sort -u emails.txt              # ordena e remove duplicados
sort -t, -k5,5 -n -r vendas.csv # separador vírgula, ordena pela 5ª coluna, numérico, decrescente
sort -V versoes.txt             # ordem de versão: 1.9 < 1.10 (GNU)
sort -o dados.txt dados.txt     # ordena "no lugar", com segurança

O uniq remove linhas repetidas adjacentes, por isso quase sempre vem depois de um sort:

Bash
sort nomes.txt | uniq           # remove duplicados
sort nomes.txt | uniq -c        # conta quantas vezes cada linha aparece
sort nomes.txt | uniq -d        # só as linhas que aparecem repetidas
sort nomes.txt | uniq -u        # só as linhas que aparecem uma única vez

O combo sort | uniq -c | sort -rn é um clássico para montar rankings. Quais produtos aparecem em mais vendas?

Bash
cut -d, -f2 vendas.csv | tail -n +2 | sort | uniq -c | sort -rn
Texto
      2 Teclado
      2 Mouse
      1 Webcam
      1 Monitor

Lendo o pipeline: pega a 2ª coluna, descarta o cabeçalho, ordena, conta os repetidos e ordena pela contagem, do maior para o menor.

cut e tr

cut recorta colunas (campos) ou caracteres de cada linha:

Bash
cut -d: -f1 /etc/passwd          # 1º campo, separado por ":" (os nomes de usuário)
cut -d, -f2,4 vendas.csv         # campos 2 e 4
cut -d, -f2-4 vendas.csv         # do campo 2 ao 4
cut -c1-10 app.log               # os 10 primeiros caracteres (ex.: a data)

O cut trata cada ocorrência do delimitador como uma separação, então não lida bem com colunas separadas por vários espaços. Para esse caso, use o awk.

tr troca ou apaga caracteres (lê só do stdin):

Bash
echo "linux" | tr 'a-z' 'A-Z'           # LINUX
echo "a  b    c" | tr -s ' '            # "a b c": espreme espaços repetidos
echo "$PATH" | tr ':' '\n'              # um diretório do PATH por linha
tr -d '\r' < windows.txt > unix.txt     # remove o \r das quebras de linha do Windows
head -c 32 /dev/urandom | base64 | tr -dc 'A-Za-z0-9' | head -c 16; echo   # senha aleatória

Atenção: o tr trabalha byte a byte, então letras acentuadas não são convertidas por tr 'a-z' 'A-Z' ("é" continua "é").

sed: editar texto em fluxo

O sed (stream editor) aplica edições em cada linha que passa por ele. O uso número um é substituir:

Bash
sed 's/http:/https:/' links.txt           # troca a 1ª ocorrência de cada linha
sed 's/http:/https:/g' links.txt          # g = todas as ocorrências da linha
sed 's/erro/ERRO/gi' app.log              # i = ignora maiúsculas (GNU)
sed 's|/usr/local|/opt|g' config.sh       # outro delimitador, para não escapar as barras
sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3\/\2\/\1/' datas.txt   # 2026-09-23 -> 23/09/2026

Na substituição, \1, \2... referenciam os grupos capturados entre parênteses, e & representa o trecho inteiro que casou.

O sed também imprime e apaga linhas por número ou por padrão:

Bash
sed -n '10,20p' arquivo.txt          # imprime só as linhas 10 a 20
sed -n '/INICIO/,/FIM/p' arquivo.txt # imprime do padrão INICIO até o padrão FIM
sed '5d' arquivo.txt                 # apaga a linha 5
sed '$d' arquivo.txt                 # apaga a última linha
sed '/^#/d' config.conf              # apaga linhas de comentário
sed '/^$/d' arquivo.txt              # apaga linhas vazias
sed 's/\r$//' script.sh              # converte quebras de linha do Windows (CRLF) para Unix (LF)

Por padrão, o sed só mostra o resultado; o arquivo original não muda. Para editar o arquivo, use -i (in-place):

Bash
sed -i 's/localhost/127.0.0.1/g' config.ini        # GNU (Linux): edita no lugar
sed -i.bak 's/localhost/127.0.0.1/g' config.ini    # edita e guarda o original em config.ini.bak
sed -i '' 's/localhost/127.0.0.1/g' config.ini     # macOS (BSD): exige o argumento de extensão

A forma -i.bak (com a extensão colada) funciona tanto no GNU quanto no BSD, então é a mais portável, e ainda deixa um backup. Antes de um sed -i em arquivo importante, rode o mesmo comando sem o -i e confira a saída.

awk: uma linguagem para colunas

O awk divide cada linha em campos (por padrão, separados por espaços ou tabs, quantos forem) e deixa você programar sobre eles. $1 é o primeiro campo, $2 o segundo, $0 a linha inteira e $NF o último campo. NR é o número da linha atual e NF, o número de campos.

Bash
awk '{print $1}' access.log                     # 1ª coluna
awk '{print $1, $NF}' arquivo.txt               # primeira e última colunas
awk -F: '{print $1, $7}' /etc/passwd            # -F define o separador
awk -F: '$3 >= 1000 && $3 != 65534 {print $1}' /etc/passwd   # usuários comuns (exclui o nobody)
awk 'NR > 1' vendas.csv                         # pula o cabeçalho
awk 'length($0) > 120' codigo.js                # linhas com mais de 120 caracteres
awk -v OFS=',' '{print $1, $3}' dados.txt       # OFS: separador de saída

A estrutura é padrão { ação }: para cada linha que satisfaz o padrão, executa a ação. Um bloco END roda depois da última linha, ideal para totais:

Bash
awk -F, 'NR > 1 { total += $4 * $5 } END { printf "Faturamento: R$ %.2f\n", total }' vendas.csv
Texto
Faturamento: R$ 2227.10

E com arrays associativos dá para agrupar, como um GROUP BY de SQL:

Bash
awk -F, 'NR > 1 { qtd[$3] += $4 } END { for (c in qtd) print c, qtd[c] }' vendas.csv
Texto
perifericos 11
video 3

A ordem de saída de um for (c in qtd) não é garantida; se precisar, passe o resultado por um sort. Para CSVs "de verdade", com vírgulas dentro de campos entre aspas, o awk -F, não basta: aí vale usar uma ferramenta própria para CSV ou uma linguagem de programação.

paste e column

Bash
paste nomes.txt idades.txt       # junta dois arquivos lado a lado, separados por tab
paste -sd, lista.txt             # junta todas as linhas numa só, separadas por vírgula
column -t -s, vendas.csv         # alinha um CSV como tabela
mount | column -t                # deixa qualquer saída em colunas mais legível
Texto
data        produto  categoria    quantidade  preco
2026-09-01  Teclado  perifericos  2           150.00
2026-09-01  Monitor  video        1           899.90
...

diff e comm

diff mostra as diferenças entre dois arquivos:

Bash
diff antigo.conf novo.conf       # formato clássico
diff -u antigo.conf novo.conf    # formato unificado (o mesmo do git diff)
diff -r pasta1/ pasta2/          # compara diretórios inteiros
diff -q pasta1/ pasta2/          # só diz QUAIS arquivos diferem
diff <(sort a.txt) <(sort b.txt) # compara saídas de comandos
Diff
--- antigo.conf	2026-09-22 18:03:11.000000000 -0300
+++ novo.conf	2026-09-23 10:41:52.000000000 -0300
@@ -1,3 +1,3 @@
 server_name exemplo.com;
-listen 80;
+listen 443 ssl;
 root /var/www/site;

Linhas com - saíram, linhas com + entraram. O código de saída do diff é 0 se os arquivos forem iguais e 1 se forem diferentes, o que é útil em scripts.

comm compara dois arquivos ordenados e separa as linhas em três colunas: só no primeiro, só no segundo e em ambos:

Bash
comm -12 <(sort lista-a.txt) <(sort lista-b.txt)   # linhas presentes nos DOIS
comm -23 <(sort lista-a.txt) <(sort lista-b.txt)   # linhas que só estão em lista-a

jq: processando JSON

APIs e ferramentas modernas falam JSON, e o jq é o sed/awk do JSON. Instale com sudo apt install jq (ou dnf, pacman, brew):

Bash
curl -s https://api.github.com/repos/torvalds/linux | jq '.'                 # formata e colore
curl -s https://api.github.com/repos/torvalds/linux | jq '.stargazers_count' # um campo
jq '.usuarios[0].nome' dados.json                  # navega em objetos e arrays
jq -r '.usuarios[].email' dados.json               # -r: texto puro, sem aspas
jq '.usuarios[] | select(.ativo == true) | .nome' dados.json   # filtra
jq '.usuarios | length' dados.json                 # conta itens

Receita: analisando um log de acesso

Juntando tudo. Um log do Nginx no formato padrão tem linhas assim:

Texto
203.0.113.7 - - [23/Sep/2026:10:15:35 -0300] "POST /api/orders HTTP/1.1" 500 87 "-" "curl/8.5.0"

Separando por espaços, o IP é o campo 1, o caminho é o campo 7 e o status HTTP é o campo 9. Com isso:

Bash
# Os 10 IPs que mais fizeram requisições
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10
 
# Quantas respostas de cada status HTTP
awk '{print $9}' access.log | sort | uniq -c | sort -rn
 
# Quantos erros 5xx
awk '$9 >= 500' access.log | wc -l
 
# Quais URLs mais dão 404
awk '$9 == 404 {print $7}' access.log | sort | uniq -c | sort -rn | head
 
# Requisições por minuto: 17 caracteres do campo 4, pulando o "[" (dd/Mmm/aaaa:hh:mm)
awk '{print substr($4, 2, 17)}' access.log | uniq -c

Em segundos, sem instalar nada, você descobre se aquele pico de tráfego foi um cliente legítimo ou um robô procurando /wp-admin e /.env.

Permissões

O Linux nasceu como sistema multiusuário: várias pessoas e vários serviços compartilham a mesma máquina, e cada arquivo precisa dizer quem pode fazer o quê com ele. Entender permissões resolve metade dos erros Permission denied que você vai encontrar.

Lendo a saída do ls -l

Texto
-rwxr-xr-- 1 gabriel devs 4096 Sep 23 10:00 deploy.sh
│└┬┘└┬┘└┬┘ │ └──┬──┘ └─┬┘ └─┬┘ └─────┬────┘ └───┬───┘
│ │  │  │  │    │      │    │        │          └─ nome
│ │  │  │  │    │      │    │        └─ data da última modificação
│ │  │  │  │    │      │    └─ tamanho em bytes
│ │  │  │  │    │      └─ grupo dono
│ │  │  │  │    └─ usuário dono
│ │  │  │  └─ número de hard links
│ │  │  └─ permissões dos outros (others): r--
│ │  └─ permissões do grupo (group): r-x
│ └─ permissões do dono (user): rwx
└─ tipo: - arquivo, d diretório, l link, c/b dispositivo, s socket, p pipe

As permissões vêm em três trios, sempre na mesma ordem: dono (user, u), grupo (group, g) e outros (others, o, todo o resto). Cada trio tem três posições: r (leitura), w (escrita) e x (execução); um - significa "não tem". Se aparecer um + depois das permissões (-rw-r--r--+), o arquivo tem ACLs, permissões mais detalhadas que você consulta com getfacl.

rwx em arquivos e em diretórios

As letras significam coisas diferentes para arquivos e diretórios:

PermissãoEm um arquivoEm um diretório
r (read)Ler o conteúdoListar os nomes dentro dele (ls)
w (write)Alterar o conteúdoCriar, apagar e renomear itens dentro dele (precisa de x também)
x (execute)Executar como programaEntrar nele (cd) e acessar os itens pelo nome

Duas consequências que surpreendem:

  • Apagar um arquivo depende da permissão do diretório, não do arquivo. Se você tem w e x na pasta, pode apagar um arquivo mesmo sem permissão nenhuma sobre ele (a exceção é o sticky bit, que você vai ver adiante).
  • Sem x em um diretório, nada dentro dele é acessível, mesmo que os arquivos tenham permissão de leitura. É por isso que diretórios quase sempre têm x junto com r.

chmod: modo simbólico

O chmod (change mode) altera permissões. No modo simbólico você diz quem (u, g, o, a = todos), a operação (+ adiciona, - remove, = define exatamente) e quais permissões:

Bash
chmod +x deploy.sh           # torna executável (para todos, respeitando o umask)
chmod u+x deploy.sh          # executável só para o dono
chmod g+w relatorio.txt      # o grupo pode escrever
chmod o-r segredo.txt        # outros não podem mais ler
chmod a=r publico.txt        # todos só podem ler
chmod u=rw,g=r,o= notas.txt  # define os três trios de uma vez (outros: nada)
chmod -R g+rX projeto/       # recursivo; X maiúsculo: execução só em diretórios

O X maiúsculo é um truque valioso: ele dá permissão de execução apenas para diretórios (e para arquivos que já são executáveis para alguém), exatamente o que você quer ao liberar leitura numa árvore inteira.

chmod: modo octal

No modo octal, cada trio vira um dígito, somando os valores r = 4, w = 2 e x = 1:

rwxContaDígito
rwx4 + 2 + 17
rw-4 + 26
r-x4 + 15
r--44
---00

Então chmod 755 significa dono 7 (rwx), grupo 5 (r-x) e outros 5 (r-x). Por baixo, são 9 bits, um para cada permissão, cada dígito octal representa exatamente três deles:

1256
1128
164
132
016
18
14
02
11
755 = rwx r-x r-x (os 9 bits de permissão)dec 493hex 0x1ED9 bits
1256
1128
064
132
016
08
14
02
01
644 = rw- r-- r-- (arquivo comum)dec 420hex 0x1A49 bits

Os valores que você vai usar 99% do tempo:

OctalSimbólicoUso típico
755rwxr-xr-xDiretórios, scripts e programas
644rw-r--r--Arquivos comuns (código, HTML, configurações não secretas)
600rw-------Segredos: chaves SSH privadas, .env, credenciais
700rwx------Diretórios privados, como ~/.ssh
400r--------Arquivos que nem o dono deve alterar (ex.: chave .pem da AWS)
775 / 664rwxrwxr-x / rw-rw-r--Pastas e arquivos compartilhados com um grupo
777rwxrwxrwxQuase nunca. Veja o aviso abaixo
Bash
chmod 644 index.html
chmod 600 ~/.ssh/id_ed25519
chmod 700 ~/.ssh
find /var/www/site -type d -exec chmod 755 {} +   # diretórios: 755
find /var/www/site -type f -exec chmod 644 {} +   # arquivos: 644

chown e chgrp

chown (change owner) muda o dono e/ou o grupo; normalmente exige sudo:

Bash
sudo chown maria relatorio.pdf              # novo dono
sudo chown maria:devs relatorio.pdf         # novo dono e novo grupo
sudo chown :devs relatorio.pdf              # só o grupo (o mesmo que chgrp devs)
sudo chown -R www-data:www-data /var/www/site   # recursivo (usuário do Nginx/Apache no Debian/Ubuntu)
sudo chown -R "$USER":"$USER" ~/projeto     # "devolve" arquivos criados como root para você
sudo chgrp devs planilha.ods                # muda só o grupo

O penúltimo exemplo é um salva-vidas: quando você roda algo com sudo (ou um contêiner Docker escreve numa pasta montada), os arquivos criados ficam com dono root, e depois você não consegue editá-los com o seu usuário.

umask: as permissões padrão

Por que um arquivo novo nasce com 644 e um diretório com 755? Por causa da umask, uma máscara que retira permissões dos valores-base (666 para arquivos, 777 para diretórios):

Bash
umask        # 0022
umask -S     # u=rwx,g=rx,o=rx

Com a umask 022, o bit de escrita é retirado do grupo e dos outros: arquivos nascem 644 e diretórios 755. Com 002, o grupo mantém a escrita (664 e 775), comum em ambientes de colaboração. Arquivos comuns nunca nascem executáveis: o x precisa ser dado explicitamente com chmod.

root, sudo e su

O root é o superusuário (UID 0). Ele ignora quase todas as verificações de permissão, pode ler e alterar qualquer arquivo e matar qualquer processo. Justamente por isso, você não deve trabalhar logado como root no dia a dia.

O sudo (superuser do) executa um comando com privilégios de root, pedindo a sua senha (não a do root) e registrando o que foi feito nos logs do sistema:

Bash
sudo apt update                  # um comando como root
sudo -u postgres psql            # um comando como OUTRO usuário
sudo -i                          # abre um shell de login como root (saia com exit)
sudo -l                          # lista o que você pode fazer com sudo
sudo -k                          # esquece a senha em cache: a próxima vez pede de novo
sudo !!                          # repete o último comando, agora com sudo

Depois que você digita a senha, o sudo a mantém em cache por alguns minutos, então comandos seguidos não pedem de novo.

O su (switch user) troca de usuário e pede a senha do usuário de destino. su - maria abre uma sessão como maria (o - carrega o ambiente completo dela). su - sozinho vira root, mas, no Ubuntu, a conta root vem sem senha definida (bloqueada para login), então o caminho é sudo -i.

Bits especiais: setuid, setgid e sticky bit

Além dos 9 bits normais, existem três bits especiais. Você raramente vai configurá-los, mas vai encontrá-los:

BitOctalOnde apareceEfeito
setuid4000s no lugar do x do dono: -rwsr-xr-xO programa roda com os privilégios do dono do arquivo, não de quem o executa
setgid2000s no lugar do x do grupo: drwxrwsr-xEm executáveis, roda com o grupo do arquivo; em diretórios, arquivos novos herdam o grupo do diretório
sticky1000t no lugar do x dos outros: drwxrwxrwtEm diretórios, só o dono do arquivo, o dono do diretório ou o root podem apagá-lo ou renomeá-lo
Bash
ls -l /usr/bin/passwd    # -rwsr-xr-x ... root root ... /usr/bin/passwd
ls -ld /tmp              # drwxrwxrwt ... root root ... /tmp
chmod g+s /srv/compartilhado    # setgid num diretório de equipe
chmod 2775 /srv/compartilhado   # o mesmo, em octal
chmod +t /srv/uploads           # sticky bit

O passwd é o exemplo clássico de setuid: um usuário comum consegue trocar a própria senha porque o programa roda como root para gravar em /etc/shadow. O /tmp usa o sticky bit para que todos possam criar arquivos ali sem poder apagar os dos outros. Se a letra aparecer maiúscula (S ou T), o bit especial está ligado, mas o x correspondente não. Por segurança, o Linux ignora o setuid em scripts interpretados (como .sh).

Usuários e grupos

Cada processo e cada arquivo pertencem a um usuário e a um grupo. Grupos são a forma de dar a mesma permissão a várias pessoas (ou serviços) de uma vez: quem está no grupo docker pode usar o Docker; quem está no grupo sudo (Debian/Ubuntu) ou wheel (Fedora, RHEL, Arch) pode usar o sudo.

Quem sou eu, quem está logado

Bash
whoami            # gabriel
id                # uid=1000(gabriel) gid=1000(gabriel) groups=1000(gabriel),4(adm),27(sudo),998(docker)
id maria          # o mesmo para outro usuário
groups            # só os grupos do usuário atual
who               # quem está logado agora
w                 # quem está logado e o que está fazendo (mais a carga do sistema)
last              # histórico de logins

Por trás dos nomes existem números: o UID (user ID) e o GID (group ID). O root é sempre UID 0. Na maioria das distros, contas de pessoas começam em 1000; números menores ficam para os usuários de sistema, criados para rodar serviços (www-data, postgres, systemd-network...).

Criar, alterar e remover usuários

Bash
sudo adduser maria                       # Debian/Ubuntu: interativo, cria a home e pede a senha
sudo useradd -m -s /bin/bash maria       # universal: -m cria a home, -s define o shell
sudo passwd maria                        # define ou troca a senha de maria
passwd                                   # troca a SUA senha
sudo usermod -aG sudo maria              # adiciona maria ao grupo sudo
sudo usermod -aG docker "$USER"          # você no grupo docker (sem sudo no docker)
sudo groupadd devs                       # cria um grupo
sudo gpasswd -d maria devs               # remove maria do grupo devs
sudo userdel -r maria                    # remove o usuário E a home dele (-r)
sudo deluser --remove-home maria         # o mesmo, no estilo Debian/Ubuntu
sudo chage -l maria                      # informações de expiração de senha

adduser e useradd parecem a mesma coisa, mas não são. No Debian e no Ubuntu, adduser é um programa amigável que faz perguntas e cria tudo; useradd é a ferramenta de baixo nível, presente em todas as distros, que só faz o que você pedir (sem -m, pode nem criar a pasta pessoal). No Fedora e no RHEL, adduser é apenas um atalho para useradd.

Os arquivos passwd, group e shadow

As contas locais ficam em arquivos de texto em /etc:

Texto
# /etc/passwd: legível por todos
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
gabriel:x:1000:1000:Gabriel Mesquita,,,:/home/gabriel:/bin/bash
│       │ │    │    │                   │             └─ shell de login
│       │ │    │    │                   └─ pasta pessoal
│       │ │    │    └─ descrição (GECOS)
│       │ │    └─ GID do grupo principal
│       │ └─ UID
│       └─ "x": a senha está em /etc/shadow
└─ nome de usuário
  • /etc/group lista os grupos no formato nome:x:GID:membros (ex.: docker:x:998:gabriel,maria).
  • /etc/shadow guarda os hashes das senhas e só pode ser lido pelo root. Os hashes começam com um prefixo que indica o algoritmo, como $6$ (SHA-512) ou $y$ (yescrypt). Um ! ou * no lugar do hash significa que o login por senha está bloqueado.
  • O shell /usr/sbin/nologin impede que contas de serviço façam login interativo.

Para consultar, prefira getent passwd maria e getent group docker: eles também funcionam quando os usuários vêm de fontes externas, como LDAP.

sudoers e visudo

Quem pode usar sudo, e para quê, é definido em /etc/sudoers e nos arquivos de /etc/sudoers.d/. Nunca edite esses arquivos diretamente: use o visudo, que valida a sintaxe antes de salvar. Um erro de sintaxe no sudoers pode deixar o sudo inutilizável, e aí você fica sem como consertar.

Bash
sudo visudo                               # edita /etc/sudoers com validação
sudo visudo -f /etc/sudoers.d/deploy      # cria/edita um arquivo separado (recomendado)
Texto
# usuário  máquinas=(como_quem:grupo)  comandos
maria      ALL=(ALL:ALL)               ALL
%sudo      ALL=(ALL:ALL)               ALL          # % indica um grupo
deploy     ALL=(ALL)  NOPASSWD: /usr/bin/systemctl restart minha-api

A última linha é um bom padrão para automação: o usuário deploy pode reiniciar apenas aquele serviço, sem senha, e nada mais.

Processos

Um processo é um programa em execução. O arquivo /usr/bin/nginx no disco é um programa; cada cópia dele rodando na memória é um processo, com seu próprio estado.

O que é um processo

Todo processo tem:

  • um PID (process ID), número único enquanto ele existir;
  • um PPID, o PID do processo pai que o criou;
  • um dono (usuário), que define o que ele pode acessar;
  • um estado: R rodando, S dormindo (esperando algo), D esperando E/S sem poder ser interrompido, T parado, Z zumbi (terminou, mas o pai ainda não recolheu o código de saída).

Os processos formam uma árvore. O primeiro processo do espaço do usuário, com PID 1, é o sistema de inicialização, o systemd, na maioria das distros atuais. Quando você digita um comando, o shell cria um processo filho (chamadas fork e exec) e espera ele terminar.

ps: uma foto dos processos

Bash
ps                         # processos do terminal atual
ps aux                     # TODOS os processos, estilo BSD
ps -ef                     # TODOS os processos, estilo System V (mostra o PPID)
ps aux --sort=-%mem | head # os que mais consomem memória (procps, Linux)
ps aux --sort=-%cpu | head # os que mais consomem CPU
ps -ef --forest            # em árvore, mostrando quem criou quem
ps -o pid,ppid,user,%cpu,%mem,cmd -p 1234   # colunas escolhidas de um PID
pstree -p                  # árvore de processos com PIDs

As colunas do ps aux:

ColunaSignificado
USERDono do processo
PIDIdentificador do processo
%CPU / %MEMUso de CPU e da memória física
VSZ / RSSMemória virtual / memória física realmente ocupada (em KiB)
TTYTerminal associado (? = nenhum, típico de serviços)
STATEstado (R, S, D, T, Z, mais modificadores)
START / TIMEQuando começou / tempo total de CPU consumido
COMMANDO comando completo

Para procurar um processo, o reflexo é ps aux | grep nginx, mas o próprio grep aparece no resultado. O truque ps aux | grep [n]ginx resolve: a expressão [n]ginx casa com "nginx", mas a linha de comando do grep contém "[n]ginx", que não casa com ela mesma. Melhor ainda é usar o pgrep, logo abaixo.

top e htop: o monitor em tempo real

top mostra os processos atualizando a cada poucos segundos, com um resumo do sistema no topo:

Tecla no topO que faz
POrdena por CPU
MOrdena por memória
1Mostra cada núcleo de CPU separadamente
cMostra o comando completo
uFiltra por usuário
kEnvia um sinal (mata) a um PID
hAjuda
qSai

O htop é uma versão mais amigável, colorida, com rolagem e mouse (instale com sudo apt install htop). Teclas úteis: F3 ou / busca, F4 filtra, F5 alterna a visão em árvore, F6 escolhe a ordenação, F9 mata o processo selecionado e F10 ou q sai.

A linha load average: 0.52, 0.61, 0.58 (também mostrada pelo uptime) é a média de processos rodando ou esperando por CPU (e, no Linux, também os que esperam E/S de disco) nos últimos 1, 5 e 15 minutos. Compare com o número de núcleos (nproc): numa máquina de 4 núcleos, uma carga de 4 significa ocupação total; de 8, significa fila.

pgrep, pkill e kill

Bash
pgrep nginx                  # PIDs dos processos chamados nginx
pgrep -a node                # PIDs e linha de comando completa
pgrep -f "node server.js"    # procura na linha de comando inteira, não só no nome
pgrep -u maria               # processos do usuário maria
kill 1234                    # pede para o processo 1234 terminar (SIGTERM)
kill -9 1234                 # mata na força (SIGKILL): último recurso
kill -HUP 1234               # envia SIGHUP (muitos serviços recarregam a configuração)
kill -0 1234 && echo "vivo"  # só testa se o processo existe
pkill -f "node server.js"    # sinal (SIGTERM) para todos que casarem com o padrão
killall firefox              # por nome exato (pacote psmisc)
kill -l                      # lista todos os sinais

Apesar do nome, kill não "mata": ele envia um sinal. Por padrão, o sinal é o SIGTERM, um pedido educado para o programa encerrar, salvando o que precisar.

Sinais

SinalNúmeroQuando é usadoPode ser tratado ou ignorado?
SIGHUP1O terminal fechou; muitos serviços usam para recarregar a configuraçãoSim
SIGINT2Ctrl+C: interromperSim
SIGQUIT3Ctrl+\: sair gerando core dumpSim
SIGKILL9Matar imediatamente, sem limpezaNão
SIGTERM15Pedido padrão de término (o padrão do kill)Sim
SIGCONT18Continuar um processo paradoSim (e sempre retoma o processo)
SIGSTOP19PausarNão
SIGTSTP20Ctrl+Z: suspender a partir do terminalSim

Os números de SIGCONT, SIGSTOP e SIGTSTP acima valem para x86 e ARM (e mudam em algumas outras arquiteturas); em scripts, prefira os nomes: kill -TERM, kill -KILL, kill -STOP.

Primeiro plano, segundo plano e jobs

Um comando normal roda em primeiro plano (foreground): o terminal fica ocupado até ele acabar. Com & no fim, ele roda em segundo plano (background) e o prompt volta na hora:

Bash
./build-demorado.sh &     # [1] 48213  (número do job e PID)
jobs                      # lista os jobs deste terminal
fg %1                     # traz o job 1 para o primeiro plano
bg %1                     # continua o job 1 em segundo plano
kill %1                   # envia SIGTERM ao job 1

Esqueceu o &? Aperte Ctrl+Z para suspender o comando e digite bg para ele continuar em segundo plano. Diferença importante: Ctrl+C interrompe (SIGINT, geralmente encerra o programa); Ctrl+Z pausa (SIGTSTP): o processo continua existindo, parado, até você usar fg ou bg. Já Ctrl+D não é sinal: ele envia "fim da entrada" (EOF) e, num prompt vazio, fecha o shell.

nohup e disown: sobrevivendo ao fim da sessão

Quando você fecha o terminal ou a conexão SSH cai, os processos iniciados naquela sessão recebem SIGHUP e normalmente morrem. Para evitar:

Bash
nohup ./processa-dados.sh > processa.log 2>&1 &   # ignora o SIGHUP desde o início
./processa-dados.sh &                              # já começou sem nohup?
disown -h %1                                       # ...marque o job para não receber SIGHUP

Sem o redirecionamento, o nohup grava a saída em nohup.out. O disown sem opções remove o job da lista do shell (e ele também deixa de receber o SIGHUP do shell). Para trabalho remoto longo, porém, a ferramenta certa é o tmux, que você vai ver na seção de SSH: com ele, você pode inclusive voltar e ver a saída depois.

Prioridade: nice e renice

A niceness vai de -20 (mais prioridade) a 19 (menos prioridade); o padrão é 0. Um processo "mais legal" (nicer) cede CPU para os outros:

Bash
nice -n 10 tar -czf backup.tar.gz /dados   # inicia com prioridade mais baixa
renice 15 -p 4242                          # muda a niceness do processo 4242 para 15
sudo renice -5 -p 4242                     # aumentar a prioridade exige root
ionice -c 3 -p 4242                        # prioridade de disco "idle" (só usa E/S ociosa)

Quem está usando a porta 3000?

O erro EADDRINUSE: address already in use :::3000 é um clássico de quem desenvolve para a web: algum processo já está escutando naquela porta, muitas vezes um servidor antigo que ficou rodando. Três formas de descobrir quem é:

Bash
lsof -i :3000              # processo usando a porta 3000 (sudo para ver os de outros usuários)
ss -ltnp | grep :3000      # sockets TCP escutando, com o processo dono
sudo fuser 3000/tcp        # só o PID
Texto
COMMAND  PID    USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
node    4242 gabriel   21u  IPv6  56789      0t0  TCP *:3000 (LISTEN)

Depois, é só encerrar: kill 4242 (ou, de uma vez, sudo fuser -k 3000/tcp). O lsof (list open files) serve para muito mais: lsof -p 4242 lista tudo que um processo tem aberto, lsof /var/log/syslog mostra quem está usando um arquivo e lsof +D /mnt/pendrive explica por que o umount diz target is busy.

Serviços com systemd

Na maioria das distribuições atuais (Ubuntu, Debian, Fedora, Arch, RHEL), o primeiro processo iniciado pelo kernel, o PID 1, é o systemd. Ele sobe os serviços na inicialização, reinicia os que caem e centraliza os logs. Um serviço (nginx, docker, ssh, postgresql) é descrito por um arquivo de unit.

systemctl: controlando serviços

Bash
sudo systemctl start nginx       # inicia agora
sudo systemctl stop nginx        # para agora
sudo systemctl restart nginx     # para e inicia de novo
sudo systemctl reload nginx      # recarrega a configuração sem derrubar conexões (se o serviço suportar)
systemctl status nginx           # estado, PID, uso de memória e últimas linhas de log
sudo systemctl enable nginx      # inicia automaticamente no boot
sudo systemctl disable nginx     # não inicia mais no boot
sudo systemctl enable --now nginx   # habilita no boot E inicia agora
systemctl is-active nginx        # responde "active" ou "inactive" (ótimo para scripts)
systemctl list-units --type=service --state=running   # serviços rodando
systemctl --failed               # units que falharam

start/stop agem agora; enable/disable definem o comportamento no próximo boot. São independentes: um serviço pode estar habilitado e parado.

journalctl: os logs do sistema

Bash
journalctl -u nginx              # logs do serviço nginx
journalctl -u nginx -f           # acompanha em tempo real (como tail -f)
journalctl -u nginx --since "1 hour ago"
journalctl -u nginx --since today
journalctl -p err -b             # só erros, desde o último boot
journalctl -xe                   # fim do log com explicações (o clássico após uma falha)
journalctl --disk-usage          # quanto espaço os logs ocupam
sudo journalctl --vacuum-time=7d # apaga logs com mais de 7 dias

Criando o seu próprio serviço

Para manter uma aplicação Node.js rodando e subindo junto com a máquina:

/etc/systemd/system/minha-api.service
INI
[Unit]
Description=Minha API em Node.js
After=network.target
 
[Service]
Type=simple
User=deploy
WorkingDirectory=/srv/minha-api
ExecStart=/usr/bin/node server.js
Restart=on-failure
Environment=NODE_ENV=production
 
[Install]
WantedBy=multi-user.target
Bash
sudo systemctl daemon-reload            # relê os arquivos de unit
sudo systemctl enable --now minha-api   # habilita e inicia
journalctl -u minha-api -f              # acompanha os logs

Gerenciamento de pacotes

Cada família de distribuições tem o seu gerenciador. Todos fazem o mesmo: baixam pacotes de repositórios confiáveis, resolvem dependências e mantêm o sistema atualizado.

update e upgrade: o ritual de todo servidor novo

No Debian e no Ubuntu, a primeira coisa que se faz ao receber uma máquina nova (uma VPS, uma instância na nuvem, um container de teste) é:

Bash
sudo apt update && sudo apt upgrade -y

São dois passos diferentes, e confundi-los é muito comum:

  • apt update não instala nada. Ele baixa a lista atualizada de pacotes disponíveis nos repositórios (o "catálogo"). Sem ele, o sistema não sabe que existem versões novas.
  • apt upgrade instala as versões novas dos pacotes que você já tem, de acordo com o catálogo baixado.
  • -y responde "sim" automaticamente à confirmação.
  • && só executa o segundo comando se o primeiro der certo.

Complementos úteis:

Bash
sudo apt full-upgrade          # upgrade que também pode remover pacotes para resolver dependências
sudo apt autoremove            # remove dependências que ninguém mais usa
sudo apt clean                 # limpa o cache de pacotes baixados
cat /var/run/reboot-required 2>/dev/null   # o sistema pede reinicialização? (Ubuntu)

apt é a interface amigável para uso interativo; apt-get e apt-cache são as ferramentas antigas, com saída estável, preferidas em scripts e Dockerfiles (apt-get update && apt-get install -y ...).

Receita: preparando um servidor Ubuntu do zero

  1. Atualize o sistema

    Bash
    sudo apt update && sudo apt upgrade -y
  2. Instale as ferramentas básicas

    Bash
    sudo apt install -y curl wget git unzip htop build-essential ca-certificates ufw fail2ban
  3. Crie um usuário sem privilégios de root

    Bash
    sudo adduser deploy
    sudo usermod -aG sudo deploy
  4. Configure o firewall (sem se trancar para fora)

    Bash
    sudo ufw allow OpenSSH        # libere o SSH ANTES de ativar
    sudo ufw allow 80/tcp
    sudo ufw allow 443/tcp
    sudo ufw enable
    sudo ufw status verbose
  5. Ative as atualizações de segurança automáticas

    Bash
    sudo apt install -y unattended-upgrades
    sudo dpkg-reconfigure --priority=low unattended-upgrades
  6. Acerte o fuso horário e reinicie se necessário

    Bash
    sudo timedatectl set-timezone America/Sao_Paulo
    [ -f /var/run/reboot-required ] && sudo reboot

Comparação entre gerenciadores

Açãoapt (Debian/Ubuntu)dnf (Fedora/RHEL)pacman (Arch)zypper (openSUSE)
Atualizar o catálogosudo apt updatesudo dnf check-updatesudo pacman -Sysudo zypper refresh
Atualizar o sistemasudo apt upgradesudo dnf upgradesudo pacman -Syusudo zypper update
Instalarsudo apt install pctsudo dnf install pctsudo pacman -S pctsudo zypper install pct
Removersudo apt remove pctsudo dnf remove pctsudo pacman -R pctsudo zypper remove pct
Buscarapt search termodnf search termopacman -Ss termozypper search termo
Detalhesapt show pctdnf info pctpacman -Si pctzypper info pct
Instaladosapt list --installeddnf list installedpacman -Qzypper search -i
Limpar cachesudo apt cleansudo dnf clean allsudo pacman -Scsudo zypper clean

Pacotes .deb, snap e flatpak

Bash
sudo apt install ./programa.deb   # instala um .deb baixado resolvendo dependências (note o ./)
sudo dpkg -i programa.deb         # instala sem resolver dependências
dpkg -l | grep nginx              # pacotes instalados com "nginx" no nome
snap install code --classic       # pacote snap (Ubuntu)
flatpak install flathub org.gimp.GIMP   # pacote flatpak

No macOS, o equivalente popular é o Homebrew: brew update, brew upgrade, brew install git.

Disco e memória

Quanto espaço tenho?

Bash
df -h                     # espaço livre por sistema de arquivos, em unidades legíveis
df -h /                   # só a partição raiz
du -sh pasta/             # tamanho total de uma pasta
du -h --max-depth=1 | sort -h      # tamanho de cada subpasta, ordenado (GNU)
sudo du -xh / --max-depth=1 2>/dev/null | sort -h   # o que ocupa a raiz, sem atravessar outros discos
ncdu /var                 # navegador interativo de uso de disco (instale: sudo apt install ncdu)

A pergunta mais comum em servidor, "o que encheu o disco?", quase sempre se resolve com du ... | sort -h descendo pasta a pasta. Suspeitos frequentes: /var/log, imagens antigas do Docker (docker system df e docker system prune) e o cache de pacotes.

Discos e partições

Bash
lsblk                     # discos e partições em árvore
lsblk -f                  # com sistema de arquivos, rótulo e UUID
sudo blkid                # UUIDs das partições
sudo fdisk -l             # tabela de partições (só listar é seguro)
mount | column -t         # o que está montado e onde
sudo mount /dev/sdb1 /mnt/dados
sudo umount /mnt/dados

Para montar automaticamente no boot, adiciona-se uma linha no /etc/fstab usando o UUID. Um erro nesse arquivo pode impedir o sistema de iniciar: depois de editar, teste com sudo mount -a antes de reiniciar.

Memória

Bash
free -h                   # RAM e swap
cat /proc/meminfo | head  # detalhes da memória
vmstat 1                  # memória, CPU e E/S a cada segundo
swapon --show             # áreas de swap ativas

Na saída do free, a coluna que importa é available: o Linux usa a RAM livre como cache de disco (coluna buff/cache) e a devolve quando os programas precisam. "Pouca memória livre" não é problema; pouca memória disponível, sim.

Rede

Endereços, rotas e interfaces

Bash
ip a                      # interfaces e endereços IP (ip addr)
ip r                      # tabela de rotas; "default via" é o gateway
ip link                   # estado das interfaces
hostname -I               # IPs da máquina (Debian/Ubuntu)
cat /etc/resolv.conf      # servidores DNS em uso
cat /etc/hosts            # nomes resolvidos localmente

O antigo ifconfig (pacote net-tools) ainda existe, mas as distribuições modernas usam o ip (iproute2).

Testando conectividade

Bash
ping -c 4 google.com      # 4 pacotes ICMP e para
tracepath google.com      # o caminho até o destino (traceroute também serve)
dig gmesquita.dev +short  # resolve o nome no DNS
dig gmesquita.dev MX      # registros de e-mail
nslookup gmesquita.dev    # alternativa ao dig
nc -zv servidor.com 5432  # a porta 5432 está aberta?

curl e wget

Bash
curl https://api.github.com                      # GET e imprime a resposta
curl -I https://gmesquita.dev                    # só os cabeçalhos (status, redirecionamentos)
curl -L https://exemplo.com                      # segue redirecionamentos
curl -o pagina.html https://exemplo.com          # salva com o nome escolhido
curl -O https://exemplo.com/arquivo.zip          # salva com o nome original
curl -s https://exemplo.com                      # silencioso (sem barra de progresso)
curl -X POST https://api.exemplo.com/users \
  -H "Content-Type: application/json" \
  -d '{"nome": "Ana"}'                           # POST com JSON
wget -c https://exemplo.com/iso.iso              # baixa retomando de onde parou

Portas abertas

Bash
ss -tulpn                 # portas TCP/UDP escutando, com processo (sudo para ver todos)
ss -tn state established  # conexões ativas
sudo netstat -tulpn       # equivalente antigo (net-tools)

Firewall com ufw

Bash
sudo ufw status verbose
sudo ufw allow 22/tcp          # ou: sudo ufw allow OpenSSH
sudo ufw allow from 203.0.113.10 to any port 5432
sudo ufw deny 3306
sudo ufw delete allow 80/tcp
sudo ufw enable

SSH e trabalho remoto

Conectando

Bash
ssh usuario@servidor.com
ssh -p 2222 usuario@servidor.com     # porta diferente da 22
ssh -i ~/.ssh/chave_aws usuario@ip   # chave específica
ssh usuario@servidor 'df -h'         # executa um comando remoto e volta

Chaves em vez de senha

  1. Gere um par de chaves

    Bash
    ssh-keygen -t ed25519 -C "seu-email@exemplo.com"

    Aceite o caminho padrão (~/.ssh/id_ed25519) e defina uma passphrase. A chave privada nunca sai da sua máquina; a pública (.pub) vai para os servidores.

  2. Copie a chave pública para o servidor

    Bash
    ssh-copy-id usuario@servidor.com
  3. Teste e desative o login por senha

    Depois de conseguir entrar sem senha, edite /etc/ssh/sshd_config no servidor, defina PasswordAuthentication no e PermitRootLogin no, e reinicie o serviço (sudo systemctl restart ssh; em algumas distribuições o serviço se chama sshd). Mantenha a sessão atual aberta até testar uma nova conexão.

~/.ssh/config: apelidos para servidores

~/.ssh/config
Texto
Host producao
    HostName 203.0.113.10
    User deploy
    Port 2222
    IdentityFile ~/.ssh/id_ed25519

Agora ssh producao basta, e o apelido também funciona no scp e no rsync.

Copiando arquivos

Bash
scp arquivo.txt producao:/tmp/                 # local → remoto
scp producao:/var/log/app.log .                # remoto → local
scp -r pasta/ producao:/srv/                   # pasta inteira
rsync -avz --progress site/ producao:/srv/site/   # sincroniza só o que mudou
rsync -avz --delete site/ producao:/srv/site/     # espelho exato (apaga no destino o que não existe na origem)

tmux: sessões que sobrevivem à queda da conexão

Bash
tmux new -s deploy     # cria a sessão "deploy"
# Ctrl+b depois d      → desanexa (a sessão continua rodando)
tmux ls                # lista sessões
tmux attach -t deploy  # volta para a sessão
# Ctrl+b %  divide na vertical, Ctrl+b "  na horizontal, Ctrl+b setas  troca de painel

Rode migrações, builds e backups longos dentro do tmux: se a internet cair, o processo continua.

Compactação e arquivos tar

Bash
tar -czvf backup.tar.gz pasta/        # cria (c) comprimido com gzip (z), verboso (v), arquivo (f)
tar -cJvf backup.tar.xz pasta/        # compressão xz (J): menor, mais lento
tar -tzvf backup.tar.gz               # lista o conteúdo (t) sem extrair
tar -xzvf backup.tar.gz               # extrai (x) no diretório atual
tar -xzvf backup.tar.gz -C /destino   # extrai em outro diretório
gzip arquivo.log                      # vira arquivo.log.gz (substitui o original)
gunzip arquivo.log.gz
zip -r site.zip site/
unzip site.zip -d destino/
zstd arquivo                          # compressão moderna e rápida (arquivo.zst)

Macete para lembrar o tar: criar, extrair, listar; z gzip, J xz, f sempre por último, seguido do nome do arquivo.

Ambiente, atalhos e produtividade

Variáveis de ambiente e PATH

Bash
env                        # todas as variáveis de ambiente
echo $HOME $USER $SHELL
echo $PATH                 # onde o shell procura os programas
export EDITOR=nano         # define para esta sessão e os processos filhos
export PATH="$HOME/.local/bin:$PATH"   # adiciona um diretório ao PATH

Para tornar permanente, coloque o export no arquivo de inicialização do seu shell e recarregue com source:

ArquivoQuando é lido
~/.bashrcTodo terminal interativo do bash (é onde vão aliases e exports do dia a dia)
~/.bash_profile / ~/.profileShells de login (SSH, login no console)
~/.zshrcTodo terminal interativo do zsh (padrão no macOS)
Bash
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

Aliases

Bash
alias ll='ls -lah'
alias gs='git status'
alias atualizar='sudo apt update && sudo apt upgrade -y'
alias                       # lista os aliases
unalias ll

Histórico

Bash
history                    # comandos anteriores numerados
history | grep docker      # procura no histórico
!!                         # repete o último comando
sudo !!                    # repete o último comando com sudo
!42                        # executa o comando número 42
!$                         # último argumento do comando anterior
!ssh                       # último comando que começou com "ssh"

Ctrl + R abre a busca reversa: digite parte do comando, aperte Ctrl + R de novo para ver resultados mais antigos e Enter para executar.

Atalhos do terminal

AtalhoAção
TabCompleta comandos, caminhos e nomes
Ctrl + CInterrompe o comando atual (SIGINT)
Ctrl + ZSuspende o comando (volte com fg ou bg)
Ctrl + DFim de entrada; numa linha vazia, sai do shell
Ctrl + LLimpa a tela
Ctrl + A / EInício / fim da linha
Ctrl + U / KApaga até o início / até o fim da linha
Ctrl + WApaga a palavra anterior
Alt + B / FVolta / avança uma palavra
Ctrl + RBusca no histórico

Shell script: automatizando tarefas

Um shell script é um arquivo de texto com comandos que o shell executa em sequência.

Primeiro script

ola.sh
Bash
#!/usr/bin/env bash
nome="${1:-mundo}"
echo "Olá, $nome! Hoje é $(date +%d/%m/%Y)."
Bash
chmod +x ola.sh
./ola.sh             # Olá, mundo! Hoje é ...
./ola.sh Gabriel     # Olá, Gabriel! Hoje é ...

A primeira linha (shebang) diz qual interpretador usar. $(comando) substitui pela saída do comando. ${1:-mundo} usa o primeiro argumento ou "mundo" se ele não existir.

Variáveis e aspas

Bash
nome="Ana Maria"          # sem espaços ao redor do =
echo "Olá, $nome"         # aspas duplas: expandem variáveis
echo 'Olá, $nome'         # aspas simples: texto literal
arquivos=$(ls *.log)
echo "$arquivos"          # sempre entre aspas para preservar espaços

Argumentos e códigos de saída

VariávelSignificado
$0Nome do script
$1, $2...Argumentos posicionais
$#Quantidade de argumentos
$@Todos os argumentos (use "$@")
$?Código de saída do último comando (0 = sucesso)
$$PID do próprio script

Condições e laços

Bash
if [[ -f "$arquivo" ]]; then
  echo "existe"
elif [[ -d "$arquivo" ]]; then
  echo "é um diretório"
else
  echo "não existe"
fi
 
for arquivo in *.log; do
  echo "Compactando $arquivo"
  gzip "$arquivo"
done
 
contador=1
while [[ $contador -le 5 ]]; do
  echo "Volta $contador"
  ((contador++))
done

Testes comuns: -f arquivo existe, -d diretório existe, -z string vazia, -n string não vazia, ==/!= comparam strings, -eq -ne -lt -le -gt -ge comparam números.

Funções e modo estrito

Bash
#!/usr/bin/env bash
set -euo pipefail   # para no primeiro erro, em variável indefinida e em falha dentro de pipes
 
log() {
  echo "[$(date +%H:%M:%S)] $*"
}
 
log "Iniciando..."

Script completo: backup com rotação

backup.sh
Bash
#!/usr/bin/env bash
set -euo pipefail
 
ORIGEM="${1:?Uso: backup.sh <pasta> [destino]}"
DESTINO="${2:-$HOME/backups}"
MANTER=7                                   # quantos backups guardar
 
mkdir -p "$DESTINO"
nome="$(basename "$ORIGEM")-$(date +%Y%m%d-%H%M%S).tar.gz"
 
echo "Compactando $ORIGEM em $DESTINO/$nome"
tar -czf "$DESTINO/$nome" -C "$(dirname "$ORIGEM")" "$(basename "$ORIGEM")"
 
# Mantém só os $MANTER mais recentes
ls -1t "$DESTINO"/*.tar.gz | tail -n +$((MANTER + 1)) | xargs -r rm --
 
echo "Pronto. Backups atuais:"
ls -lh "$DESTINO"

Linha por linha: ${1:?mensagem} aborta com a mensagem se faltar o argumento; o nome leva data e hora; tar -C evita gravar o caminho absoluto; ls -1t lista do mais novo para o mais velho, tail -n +8 pega do oitavo em diante e xargs -r rm apaga (o -r evita rodar rm sem argumentos). Use o ShellCheck para revisar seus scripts: ele aponta quase todos os erros clássicos.

Agendando tarefas com cron

Bash
crontab -e      # edita as tarefas do seu usuário
crontab -l      # lista
sudo crontab -e # tarefas do root

Cada linha tem cinco campos de tempo e o comando:

Texto
┌───────── minuto (0-59)
│ ┌─────── hora (0-23)
│ │ ┌───── dia do mês (1-31)
│ │ │ ┌─── mês (1-12)
│ │ │ │ ┌─ dia da semana (0-7; 0 e 7 = domingo)
│ │ │ │ │
* * * * *  comando
ExpressãoQuando roda
*/5 * * * *A cada 5 minutos
0 * * * *De hora em hora
30 2 * * *Todo dia às 02:30
0 9 * * 1-5Dias úteis às 09:00
0 0 1 * *Todo dia 1º, à meia-noite
@rebootUma vez, ao ligar a máquina
Texto
30 2 * * * /home/gabriel/backup.sh /srv/app >> /home/gabriel/backup.log 2>&1

O cron roda com um ambiente mínimo: use caminhos absolutos e redirecione a saída para um log, senão os erros somem. Em sistemas com systemd, os timers são a alternativa moderna, com logs no journalctl.

Comandos que salvam sua vida

Situações reais do dia a dia e o comando que resolve cada uma.

Esqueci o sudo. Rodou apt install htop e levou "Permission denied"? sudo !! repete o comando anterior com sudo.

Quero voltar para a pasta onde eu estava. cd - alterna entre o diretório atual e o anterior.

Preciso achar aquele comando enorme que rodei semana passada. Ctrl + R e digite um pedaço dele.

O servidor está dando erro e quero ver o log ao vivo. tail -f /var/log/nginx/error.log, ou journalctl -u nginx -f para serviços do systemd.

A porta 3000 está ocupada. lsof -i :3000 mostra o PID; kill PID encerra.

Um processo travou e não responde ao kill. kill -9 PID (SIGKILL) é o último recurso: o processo morre sem chance de salvar nada.

O disco encheu. df -h mostra qual partição; sudo du -xh / --max-depth=1 | sort -h mostra onde está o peso. Desça pasta a pasta.

Quais são os maiores arquivos daqui para baixo? find . -type f -size +100M -exec ls -lh {} +.

Preciso rodar algo que demora e a conexão SSH pode cair. Abra um tmux antes, ou use nohup comando &.

Rodei algo pesado no primeiro plano e quero o terminal de volta. Ctrl + Z e depois bg.

Quero acompanhar um comando se atualizando. watch -n 1 df -h roda a cada segundo.

O terminal ficou cheio de caracteres estranhos (depois de dar cat num binário). Digite reset e Enter, mesmo sem ver o que escreve.

Não sei se o serviço subiu. systemctl status nginx e, se falhou, journalctl -xe.

Onde está esse texto no projeto? grep -rn "TODO" src/ mostra arquivo e linha.

Quero apagar todos os arquivos .tmp de uma árvore. find . -name "*.tmp" -type f -print para conferir, depois troque -print por -delete.

Preciso copiar mantendo permissões e datas. cp -a origem destino, ou rsync -a para pastas grandes.

Esse script não roda: "Permission denied". chmod +x script.sh.

Qual versão do sistema é essa? cat /etc/os-release e uname -r (versão do kernel).

O site responde? curl -I https://site.com mostra o status HTTP e os redirecionamentos.

O que o kernel reclamou? sudo dmesg -T | tail mostra mensagens do kernel com data legível (discos, USB, falta de memória).

Há quanto tempo a máquina está ligada e qual a carga? uptime.

Qual programa responde por esse comando? type comando ou which comando.

O que mudou entre dois arquivos de configuração? diff -u antigo.conf novo.conf.

Criar uma árvore inteira de pastas de uma vez. mkdir -p projeto/{src,tests,docs}.

Virar root por um tempo. sudo -i abre um shell de root; exit volta.

Comandos perigosos

O famoso fork bomb abaixo cria processos que se multiplicam até travar a máquina. Está aqui para você reconhecê-lo quando alguém mandar "um comando legal para testar", nunca para rodar:

Bash
:(){ :|:& };:

Ele define uma função chamada : que chama a si mesma duas vezes em segundo plano, e depois a executa. Limites de processos por usuário (ulimit -u) contêm o estrago.

Tabela completa de comandos Linux

A cola definitiva deste guia, organizada por categoria. É a seção para salvar nos favoritos e consultar sempre que for subir um servidor, depurar um problema ou lembrar de um comando.

Cheat sheet

Linux: comandos essenciais

Da primeira atualização do servidor ao diagnóstico em produção.

Subindo um servidor novo

O que rodar logo no primeiro acesso

ComandoDescrição
sudo apt updateAtualiza a lista de pacotes disponíveis
sudo apt upgrade -yInstala as atualizações dos pacotes
sudo apt update && sudo apt upgrade -yOs dois de uma vez (o ritual clássico)
sudo apt full-upgrade -yUpgrade resolvendo dependências novas
sudo apt autoremove -yRemove dependências que sobraram
sudo apt install -y curl git htop ufwFerramentas básicas
sudo apt install -y build-essentialCompiladores (gcc, make)
sudo adduser deployCria um usuário para a aplicação
sudo usermod -aG sudo deployDá permissão de sudo ao usuário
sudo ufw allow OpenSSHLibera o SSH no firewall
sudo ufw enableAtiva o firewall
sudo timedatectl set-timezone America/Sao_PauloAjusta o fuso horário
sudo hostnamectl set-hostname app-01Define o nome da máquina
sudo rebootReinicia

Navegação

Ande pelo sistema de arquivos

ComandoDescrição
pwdMostra o diretório atual
lsLista arquivos
ls -lahLista tudo, com detalhes e tamanhos legíveis
ls -ltOrdena pelos mais recentes
cd pastaEntra numa pasta
cd ..Sobe um nível
cd ~ ou cdVai para a sua home
cd -Volta para o diretório anterior
tree -L 2Árvore de diretórios, 2 níveis
realpath arquivoCaminho absoluto de um arquivo

Arquivos e diretórios

Criar, copiar, mover e apagar

ComandoDescrição
touch arquivo.txtCria arquivo vazio (ou atualiza a data)
mkdir -p a/b/cCria pastas, incluindo as intermediárias
cp origem destinoCopia arquivo
cp -r pasta destinoCopia pasta inteira
cp -a origem destinoCopia preservando permissões e datas
mv antigo novoMove ou renomeia
rm arquivoApaga arquivo
rm -r pastaApaga pasta e conteúdo
rm -i arquivoPede confirmação antes
ln -s alvo atalhoCria link simbólico
file arquivoDescobre o tipo do arquivo
stat arquivoMetadados completos

Ver e editar arquivos

Ler sem abrir editor

ComandoDescrição
cat arquivoMostra o conteúdo inteiro
less arquivoNavega (/ busca, q sai)
head -n 20 arquivoPrimeiras 20 linhas
tail -n 50 arquivoÚltimas 50 linhas
tail -f app.logAcompanha o arquivo ao vivo
less +F app.logComo tail -f, mas navegável
wc -l arquivoConta linhas
nano arquivoEditor simples
vim arquivoEditor avançado (:wq salva e sai)

Busca

Encontre arquivos e textos

ComandoDescrição
find . -name "*.log"Arquivos por nome
find . -iname "*relatorio*"Nome sem diferenciar maiúsculas
find . -type d -name node_modulesSó diretórios
find . -size +100MArquivos maiores que 100 MB
find . -mtime -1Modificados nas últimas 24 h
find . -name "*.tmp" -deleteApaga os encontrados (cuidado)
grep "erro" app.logLinhas que contêm "erro"
grep -rn "TODO" src/Recursivo, com número da linha
grep -i "erro" app.logIgnora maiúsculas
grep -v "debug" app.logLinhas que NÃO contêm
grep -c "erro" app.logConta ocorrências
which nodeOnde está o executável

Redirecionamento e pipes

Ligue comandos entre si

ComandoDescrição
cmd > arquivoSaída para arquivo (sobrescreve)
cmd >> arquivoSaída para arquivo (acrescenta)
cmd 2> erros.logSó os erros para arquivo
cmd > saida.log 2>&1Saída e erros no mesmo arquivo
cmd &> saida.logAtalho do bash para o anterior
cmd > /dev/null 2>&1Descarta tudo
cmd1 | cmd2Saída de um vira entrada do outro
cmd | tee arquivoMostra na tela e grava
cmd1 && cmd2Roda o segundo se o primeiro der certo
cmd1 || cmd2Roda o segundo se o primeiro falhar
find . -name "*.log" | xargs rmUsa a lista como argumentos

Processar texto

Filtrar, ordenar e transformar

ComandoDescrição
sort arquivoOrdena linhas
sort -n / sort -hNumérico / tamanhos legíveis
uniq -cConta repetições (ordene antes)
cut -d, -f2 dados.csvSegunda coluna de um CSV
tr 'a-z' 'A-Z'Troca caracteres
sed 's/antigo/novo/g' arqSubstitui texto
sed -i 's/8080/3000/' confSubstitui no próprio arquivo
awk '{print $1}' access.logPrimeira coluna
awk -F: '{print $1}' /etc/passwdSeparador personalizado
diff -u antigo novoDiferença entre arquivos
jq '.nome' dados.jsonExtrai campo de JSON

Permissões

Quem pode ler, escrever e executar

ComandoDescrição
ls -lMostra permissões (rwxr-xr-x)
chmod +x script.shTorna executável
chmod 755 script.shDono tudo, outros ler e executar
chmod 644 arquivoDono lê e escreve, outros só leem
chmod 600 ~/.ssh/id_ed25519Só o dono lê e escreve
chmod -R u+rw pastaRecursivo, modo simbólico
chown usuario:grupo arquivoMuda dono e grupo
chown -R deploy:deploy /srv/appRecursivo
umaskPermissão padrão de novos arquivos
sudo comandoExecuta como root

Usuários e grupos

Contas do sistema

ComandoDescrição
whoamiSeu usuário
idUID, GID e grupos
groups usuarioGrupos de um usuário
sudo adduser nomeCria usuário (interativo)
sudo usermod -aG docker nomeAdiciona a um grupo
sudo passwd nomeTroca a senha
sudo deluser nomeRemove usuário
su - nomeTroca de usuário
sudo -iShell de root
sudo visudoEdita o sudoers com segurança
who / wQuem está logado

Processos

Monitorar e encerrar

ComandoDescrição
ps auxTodos os processos
ps aux | grep nodeProcura um processo
top / htopMonitor em tempo real
pgrep -a nodePIDs por nome
kill PIDPede para encerrar (SIGTERM)
kill -9 PIDForça o encerramento (SIGKILL)
pkill -f "node server"Encerra por padrão
comando &Roda em segundo plano
jobs / fg / bgGerencia tarefas do shell
nohup comando &Sobrevive ao logout
lsof -i :3000Quem usa a porta 3000

Serviços (systemd)

Iniciar, parar e ver logs

ComandoDescrição
systemctl status nginxEstado do serviço
sudo systemctl start nginxInicia
sudo systemctl stop nginxPara
sudo systemctl restart nginxReinicia
sudo systemctl reload nginxRecarrega a configuração
sudo systemctl enable --now nginxLiga no boot e inicia agora
sudo systemctl disable nginxNão inicia no boot
systemctl --failedServiços com falha
sudo systemctl daemon-reloadRelê arquivos de unit
journalctl -u nginx -fLogs do serviço ao vivo
journalctl -xeFim do log com detalhes

Pacotes (apt, dnf, pacman)

Instalar e atualizar software

ComandoDescrição
sudo apt install nginxInstala (Debian/Ubuntu)
sudo apt remove nginxRemove
sudo apt purge nginxRemove com as configurações
apt search termoProcura pacotes
apt show nginxDetalhes do pacote
apt list --installedPacotes instalados
sudo apt install ./app.debInstala um .deb local
sudo dnf upgradeAtualiza tudo (Fedora/RHEL)
sudo dnf install nginxInstala (Fedora/RHEL)
sudo pacman -SyuAtualiza tudo (Arch)
sudo pacman -S nginxInstala (Arch)

Disco e memória

Espaço e uso de recursos

ComandoDescrição
df -hEspaço livre por partição
du -sh pastaTamanho de uma pasta
du -h --max-depth=1 | sort -hMaiores subpastas
ncdu /Explorador interativo de espaço
lsblk -fDiscos, partições e sistemas de arquivos
sudo fdisk -lTabela de partições
sudo mount /dev/sdb1 /mntMonta partição
sudo umount /mntDesmonta
free -hMemória RAM e swap
vmstat 1CPU, memória e E/S por segundo

Rede

Endereços, testes e portas

ComandoDescrição
ip aInterfaces e IPs
ip rRotas e gateway
ping -c 4 hostTesta se o host responde
dig dominio.com +shortConsulta DNS
curl -I https://site.comStatus e cabeçalhos HTTP
curl -L -o arq URLBaixa seguindo redirecionamentos
wget -c URLBaixa e retoma downloads
ss -tulpnPortas escutando e processos
nc -zv host 5432Testa uma porta
tracepath hostCaminho até o destino
sudo ufw statusRegras do firewall

SSH e transferência

Acesso remoto seguro

ComandoDescrição
ssh usuario@hostConecta
ssh -p 2222 usuario@hostPorta diferente
ssh-keygen -t ed25519 -C "email"Gera chave
ssh-copy-id usuario@hostInstala sua chave no servidor
scp arq usuario@host:/tmp/Copia para o servidor
scp usuario@host:/var/log/app.log .Copia do servidor
rsync -avz pasta/ host:/destino/Sincroniza só o que mudou
rsync -avzn --delete a/ host:b/Simula um espelho antes de rodar
tmux new -s nomeSessão que sobrevive à queda
tmux attach -t nomeVolta para a sessão

Compactação

tar, gzip e zip

ComandoDescrição
tar -czvf arq.tar.gz pasta/Cria .tar.gz
tar -xzvf arq.tar.gzExtrai .tar.gz
tar -xzvf arq.tar.gz -C /destinoExtrai em outro lugar
tar -tzvf arq.tar.gzLista sem extrair
tar -cJvf arq.tar.xz pasta/Cria .tar.xz (menor)
gzip arq / gunzip arq.gzComprime / descomprime
zip -r arq.zip pasta/Cria .zip
unzip arq.zip -d destino/Extrai .zip

Atalhos do terminal

Menos digitação, mais velocidade

ComandoDescrição
TabCompleta nomes e comandos
Ctrl+CInterrompe o comando
Ctrl+ZSuspende (depois bg ou fg)
Ctrl+DSai do shell
Ctrl+LLimpa a tela
Ctrl+A / Ctrl+EInício / fim da linha
Ctrl+U / Ctrl+KApaga até o início / fim
Ctrl+WApaga a palavra anterior
Ctrl+RBusca no histórico

Histórico e ambiente

Reaproveite o que já digitou

ComandoDescrição
history | grep sshProcura no histórico
!!Repete o último comando
sudo !!Repete com sudo
!$Último argumento anterior
!42Executa o comando 42 do histórico
echo $PATHOnde o shell procura programas
export VAR=valorDefine variável de ambiente
alias ll='ls -lah'Cria um atalho
source ~/.bashrcRecarrega a configuração

Informações do sistema

O que é esta máquina?

ComandoDescrição
cat /etc/os-releaseDistribuição e versão
uname -aKernel e arquitetura
hostnamectlNome, sistema e kernel
uptimeTempo ligado e carga
lscpuDetalhes da CPU
nprocNúmero de núcleos
lsusb / lspciDispositivos USB / PCI
sudo dmesg -T | tailMensagens recentes do kernel
dateData e hora
crontab -lTarefas agendadas

Referências e leituras recomendadas

Se você trabalha com código, o próximo passo natural é o guia completo de Git e GitHub, e para entender o que está por baixo dos programas que roda no terminal, o guia de linguagem C.

Teste seus conhecimentos
1 / 7

7 perguntas rápidas sobre o terminal Linux.

Qual a diferença entre apt update e apt upgrade?

Não foi dessa vez. Explicação:

O update atualiza o catálogo; sem ele, o upgrade não sabe que existem versões novas.