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-alpineouubuntusã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
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:
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:
┌──────────────────────────────────────────────────────────┐
│ 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/shaponta 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:
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:
wsl --installReinicie 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):
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 WSLArquivos 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.
O macOS não é Linux, mas é um Unix (kernel XNU, base Darwin, com utilitários vindos do BSD). Abra o Terminal com Cmd+Espaço e digite "Terminal". Desde o macOS Catalina, o shell padrão é o zsh, e o prompt termina com % em vez de $.
A maioria dos comandos deste guia funciona igual. As diferenças aparecem em flags, porque o macOS usa as versões BSD das ferramentas, enquanto as distros Linux usam as versões GNU:
Se quiser as versões GNU no Mac, instale pelo Homebrew: brew install coreutils gnu-sed. Elas ficam disponíveis com o prefixo g (gls, gdate, gsed), sem substituir as originais.
Para ter um Linux de verdade no Mac, use um contêiner Docker (veja abaixo) ou uma máquina virtual.
Você já está em casa. Na maioria dos ambientes gráficos (Ubuntu, Mint e muitos outros), Ctrl+Alt+T abre o terminal. Se não funcionar, procure por "Terminal" no menu de aplicativos.
Lembre-se também dos consoles virtuais em modo texto (Ctrl+Alt+F3, por exemplo): se a interface gráfica travar, dá para entrar por ali, investigar e reiniciar o que for preciso.
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:
ssh ubuntu@203.0.113.10 # usuário@endereço
ssh -i ~/Downloads/minha-chave.pem ubuntu@203.0.113.10 # com arquivo de chaveO 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:
docker run -it --rm ubuntu bash-imantém a entrada padrão aberta (modo interativo);-taloca um pseudo-terminal, para o prompt funcionar direito;--rmapaga o contêiner quando você sair;ubuntué a imagem ebashé 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:
apt update && apt install -y curl iputils-ping vim lessQuer 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:
docker run -it --rm -v "$PWD":/trabalho -w /trabalho ubuntu bashAnatomia 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:
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árioO 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
ls -l -h /var/loglsé o comando, o programa que vai rodar.-le-hsão opções (ou flags): mudam o comportamento do comando. Aqui,-lpede o formato longo e-hpede 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 quels -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) els -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"oucd 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:
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 manO manual é dividido em seções numeradas. As que você mais vai usar:
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:
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:
ls --help
ls --help | less # se for longo demais
grep --help | grep -- "-v" # procura a explicação de uma opção específicahelp, 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":
sudo apt install tldr # ou: npm install -g tldr
tldr tar # na primeira vez, pode ser preciso rodar tldr --updateProcurar comandos pelo assunto:
apropos compress # procura "compress" nas descrições do manual (o mesmo que man -k)
whatis ls # descrição de uma linha do comandoDescobrir o que um nome realmente é:
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 rodarE 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:
/
├── 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/UbuntuOs 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) oumessages(família Red Hat),auth.log,nginx/... É o primeiro lugar a olhar quando algo dá errado (junto com ojournalctl, 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/locale/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./proce/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":
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 base64Os 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ãoprojetos/appaponta para/home/gabriel/projetos/app.
Alguns atalhos valem em qualquer caminho:
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. Usels -apara 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.pdfeRELATORIO.pdfpodem 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: oimport Header from './header'funciona no seu Mac, mas quebra no CI em Linux, porque o arquivo se chamaHeader.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.pdfourelatorio_final.pdf. - Extensões são só convenção. O Linux não precisa de
.exepara executar nada; o que torna um arquivo executável é a permissão. O comandofiledescobre o tipo real olhando o conteúdo. - Os únicos caracteres proibidos num nome de arquivo são a barra (
/) e o byte nulo.
Navegação: pwd, cd, ls e tree
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:
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 aspasO 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:
As combinações mais úteis no dia a dia:
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 /etcA saída de ls -l tem muita informação:
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.txtCada 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:
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.
├── package.json
├── public
│ └── favicon.ico
└── src
├── app.ts
└── utils
└── format.ts
4 directories, 4 filesTab 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:
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.gzO 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 umrm *.log, rodeecho *.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:
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.mdO 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
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 criadaO 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
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)Dois detalhes que pegam muita gente:
- O
cpsobrescreve sem avisar. Se o destino existir, ele é substituído em silêncio. Use-iquando estiver em dúvida. - Copiar uma pasta depende de o destino existir.
cp -r fotos backupcriabackupcomo cópia defotossebackupnão existir; se já existir, criabackup/fotos. Para copiar só o conteúdo de uma pasta para dentro de outra, usecp -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:
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):
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
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.
Links: hard links, links simbólicos e inodes
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".
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:
echo "olá" > original.txt
ln original.txt dura.txt # hard link
ln -s original.txt simbolico.txt # link simbólico
ls -li1311234 -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.txtRepare: 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:
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:
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-fsubstitui o link se ele já existir;-ntrata 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 atalhoouunlink 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:
$ 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:
$ 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 -0300As 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 -lmostra. - 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
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 vaziasO 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:
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 comandoO 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
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.
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é-fcom persistência: se o arquivo for rotacionado (renomeado e recriado, como faz ologrotate), otailreabre o arquivo novo.--line-bufferedfaz ogrepentregar cada linha assim que a encontra; sem isso, quando a saída dogrepvai 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
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 pastanano: 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.
nano /etc/hosts # vai reclamar de permissão ao salvar...
sudo nano /etc/hosts # ...então edite arquivos do sistema com sudoUma 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.
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]:
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êntesesUm 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:
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_modulesNo -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:
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 agoraA 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:
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 .gzUma combinação que vale decorar para ler arquivos de configuração sem comentários e sem linhas vazias:
grep -Ev '^(#|$)' /etc/ssh/sshd_configExpressõ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:
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.
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), umgreprecursivo por padrão, muito rápido, que respeita o.gitignoree pula arquivos ocultos e binários. Em um projeto JavaScript, isso significa ignorar onode_modulessem você pedir. - fd, um
findsimplificado, com sintaxe mais curta, cores e também respeitando o.gitignore.
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óriosElas 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?":
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):
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
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 arquivoO > é 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:
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:
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 executadoO 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:
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:
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 pastasPor 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:
./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 rootO último exemplo resolve um problema clássico. Isto não funciona:
sudo echo "192.168.0.50 meuapp.local" >> /etc/hosts # Permission deniedO 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:
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-0usa o byte nulo como separador; combine sempre comfind -print0para suportar nomes com espaços e quebras de linha.-n 1passa um argumento por execução.-I {}define um marcador que é substituído por cada linha (implica uma execução por linha).-P 4roda 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:
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 aninhadoVocê 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:
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 ;
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 falharCuidado 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:
ls /etc > /dev/null; echo $? # 0
ls /nao-existe 2> /dev/null; echo $? # 2O 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:
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.90sort e uniq
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çaO uniq remove linhas repetidas adjacentes, por isso quase sempre vem depois de um sort:
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 vezO combo sort | uniq -c | sort -rn é um clássico para montar rankings. Quais produtos aparecem em mais vendas?
cut -d, -f2 vendas.csv | tail -n +2 | sort | uniq -c | sort -rn 2 Teclado
2 Mouse
1 Webcam
1 MonitorLendo 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:
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):
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óriaAtençã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:
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/2026Na 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:
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):
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ãoA 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.
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ídaA 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:
awk -F, 'NR > 1 { total += $4 * $5 } END { printf "Faturamento: R$ %.2f\n", total }' vendas.csvFaturamento: R$ 2227.10E com arrays associativos dá para agrupar, como um GROUP BY de SQL:
awk -F, 'NR > 1 { qtd[$3] += $4 } END { for (c in qtd) print c, qtd[c] }' vendas.csvperifericos 11
video 3A 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
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íveldata 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:
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--- 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:
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-ajq: 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):
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 itensReceita: analisando um log de acesso
Juntando tudo. Um log do Nginx no formato padrão tem linhas assim:
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:
# 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 -cEm 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
-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 pipeAs 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:
Duas consequências que surpreendem:
- Apagar um arquivo depende da permissão do diretório, não do arquivo. Se você tem
wexna pasta, pode apagar um arquivo mesmo sem permissão nenhuma sobre ele (a exceção é o sticky bit, que você vai ver adiante). - Sem
xem 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êmxjunto comr.
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:
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óriosO 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:
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:
Os valores que você vai usar 99% do tempo:
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: 644chown e chgrp
chown (change owner) muda o dono e/ou o grupo; normalmente exige sudo:
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 grupoO 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):
umask # 0022
umask -S # u=rwx,g=rx,o=rxCom 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:
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 sudoDepois 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:
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 bitO 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
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 loginsPor 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
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 senhaadduser 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:
# /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/grouplista os grupos no formatonome:x:GID:membros(ex.:docker:x:998:gabriel,maria)./etc/shadowguarda 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/nologinimpede 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.
sudo visudo # edita /etc/sudoers com validação
sudo visudo -f /etc/sudoers.d/deploy # cria/edita um arquivo separado (recomendado)# 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-apiA ú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:
Rrodando,Sdormindo (esperando algo),Desperando E/S sem poder ser interrompido,Tparado,Zzumbi (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
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 PIDsAs colunas do ps aux:
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:
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
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 sinaisApesar 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
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:
./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 1Esqueceu 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:
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 SIGHUPSem 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:
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 é:
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 PIDCOMMAND 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
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 falharamstart/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
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 diasCriando o seu próprio serviço
Para manter uma aplicação Node.js rodando e subindo junto com a máquina:
[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.targetsudo systemctl daemon-reload # relê os arquivos de unit
sudo systemctl enable --now minha-api # habilita e inicia
journalctl -u minha-api -f # acompanha os logsGerenciamento 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) é:
sudo apt update && sudo apt upgrade -ySão dois passos diferentes, e confundi-los é muito comum:
apt updatenã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 upgradeinstala as versões novas dos pacotes que você já tem, de acordo com o catálogo baixado.-yresponde "sim" automaticamente à confirmação.&&só executa o segundo comando se o primeiro der certo.
Complementos úteis:
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
Atualize o sistema
sudo apt update && sudo apt upgrade -yInstale as ferramentas básicas
sudo apt install -y curl wget git unzip htop build-essential ca-certificates ufw fail2banCrie um usuário sem privilégios de root
sudo adduser deploy sudo usermod -aG sudo deployConfigure o firewall (sem se trancar para fora)
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 verboseAtive as atualizações de segurança automáticas
sudo apt install -y unattended-upgrades sudo dpkg-reconfigure --priority=low unattended-upgradesAcerte o fuso horário e reinicie se necessário
sudo timedatectl set-timezone America/Sao_Paulo [ -f /var/run/reboot-required ] && sudo reboot
Comparação entre gerenciadores
Pacotes .deb, snap e flatpak
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 flatpakNo macOS, o equivalente popular é o Homebrew: brew update, brew upgrade, brew install git.
Disco e memória
Quanto espaço tenho?
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
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/dadosPara 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
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 ativasNa 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
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 localmenteO antigo ifconfig (pacote net-tools) ainda existe, mas as distribuições modernas usam o ip (iproute2).
Testando conectividade
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
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 parouPortas abertas
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
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 enableSSH e trabalho remoto
Conectando
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 voltaChaves em vez de senha
Gere um par de chaves
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.Copie a chave pública para o servidor
ssh-copy-id usuario@servidor.comTeste e desative o login por senha
Depois de conseguir entrar sem senha, edite
/etc/ssh/sshd_configno servidor, definaPasswordAuthentication noePermitRootLogin no, e reinicie o serviço (sudo systemctl restart ssh; em algumas distribuições o serviço se chamasshd). Mantenha a sessão atual aberta até testar uma nova conexão.
~/.ssh/config: apelidos para servidores
Host producao
HostName 203.0.113.10
User deploy
Port 2222
IdentityFile ~/.ssh/id_ed25519Agora ssh producao basta, e o apelido também funciona no scp e no rsync.
Copiando arquivos
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
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 painelRode migrações, builds e backups longos dentro do tmux: se a internet cair, o processo continua.
Compactação e arquivos tar
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
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 PATHPara tornar permanente, coloque o export no arquivo de inicialização do seu shell e recarregue com source:
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrcAliases
alias ll='ls -lah'
alias gs='git status'
alias atualizar='sudo apt update && sudo apt upgrade -y'
alias # lista os aliases
unalias llHistórico
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
Shell script: automatizando tarefas
Um shell script é um arquivo de texto com comandos que o shell executa em sequência.
Primeiro script
#!/usr/bin/env bash
nome="${1:-mundo}"
echo "Olá, $nome! Hoje é $(date +%d/%m/%Y)."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
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çosArgumentos e códigos de saída
Condições e laços
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++))
doneTestes 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
#!/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
#!/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
crontab -e # edita as tarefas do seu usuário
crontab -l # lista
sudo crontab -e # tarefas do rootCada linha tem cinco campos de tempo e o comando:
┌───────── 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)
│ │ │ │ │
* * * * * comando30 2 * * * /home/gabriel/backup.sh /srv/app >> /home/gabriel/backup.log 2>&1O 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:
:(){ :|:& };: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.
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
Navegação
Ande pelo sistema de arquivos
Arquivos e diretórios
Criar, copiar, mover e apagar
Ver e editar arquivos
Ler sem abrir editor
Busca
Encontre arquivos e textos
Redirecionamento e pipes
Ligue comandos entre si
Processar texto
Filtrar, ordenar e transformar
Permissões
Quem pode ler, escrever e executar
Usuários e grupos
Contas do sistema
Processos
Monitorar e encerrar
Serviços (systemd)
Iniciar, parar e ver logs
Pacotes (apt, dnf, pacman)
Instalar e atualizar software
Disco e memória
Espaço e uso de recursos
Rede
Endereços, testes e portas
SSH e transferência
Acesso remoto seguro
Compactação
tar, gzip e zip
Atalhos do terminal
Menos digitação, mais velocidade
Histórico e ambiente
Reaproveite o que já digitou
Informações do sistema
O que é esta máquina?
Referências e leituras recomendadas
- Linux man-pages (man7.org): as páginas de manual de todos os comandos e chamadas de sistema.
- GNU Coreutils: o manual de
ls,cp,sort,cute companhia. - GNU Bash Reference Manual: tudo sobre o shell bash.
- The Linux Command Line, de William Shotts: livro completo e gratuito sobre o terminal.
- Arch Wiki: a melhor documentação de Linux da internet, útil para qualquer distribuição.
- Documentação do Ubuntu Server: guias oficiais para servidores Ubuntu.
- Filesystem Hierarchy Standard: a especificação da árvore de diretórios.
- Instalando o WSL (Microsoft): Linux dentro do Windows.
- explainshell: cole um comando e veja cada parte explicada.
- tldr pages: exemplos práticos e curtos de cada comando.
- Documentação do systemd: referência do gerenciador de serviços.
- Manual do OpenSSH: referência do SSH e das suas ferramentas.
- ShellCheck: analisador que encontra erros em shell scripts.
- crontab.guru: monte e confira expressões do cron.
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.
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.



