Shell
Variáveis, pipes, redirecionamentos e globbing, a linha de comando como ferramenta de poder.
bash, zsh e sh: qual usar e por quê
O shell é o intérprete que lê seus comandos, faz expansões (variáveis, globs, etc.) e pede ao kernel que execute os programas. Existem vários shells — cada um com suas características.
bash (Bourne Again Shell) — o padrão na maioria
das distros Linux. Compatível com scripts sh.
Configurado em ~/.bashrc (shells interativos
não-login) e ~/.bash_profile (shells de login). Se
você não sabe qual usar, use bash.
zsh — mais features que bash: autocompletion
inteligente, correção automática de typos, themes via Oh My Zsh,
melhor histórico compartilhado entre sessões. Padrão no macOS
desde Catalina. Configurado em ~/.zshrc.
sh (POSIX shell) — o mínimo compatível com o
padrão POSIX. Use o shebang #!/bin/sh em scripts que
precisam rodar em qualquer sistema Unix (incluindo sistemas sem
bash instalado).
fish (Friendly Interactive Shell) — muito amigável para uso interativo: syntax highlighting em tempo real, sugestões de autocompletion. Porém não é POSIX-compatível — scripts fish não rodam em bash.
# Ver qual shell você está usando
$ echo $SHELL
/bin/bash
$ echo $0
bash
# Ver shells instalados no sistema
$ cat /etc/shells
/bin/sh
/bin/bash
/usr/bin/bash
/bin/zsh
/usr/bin/zsh
/usr/bin/fish
# Mudar shell padrão permanentemente
$ chsh -s /usr/bin/zsh
# (precisa fazer logout/login para ter efeito)
# Diferença de shebang:
#!/bin/bash # script usa features bash-específicas
#!/bin/sh # script usa apenas POSIX — mais portável
#!/usr/bin/env bash # forma mais portável de referenciar bash
- bash: padrão no Linux — aprenda bem antes de migrar
- zsh: mais features interativas; padrão no macOS
- sh: scripts portáveis — use quando precisar rodar em qualquer Unix
- fish: ótimo interativo, mas não-POSIX — evite para scripts
-
chsh -s /caminho/shellmuda o shell padrão do usuário
Variáveis de ambiente: $PATH, $HOME, $USER
Variáveis de ambiente são pares chave=valor que o shell mantém e passa para processos filhos. Elas configuram o comportamento do sistema e dos programas.
$PATH — lista de diretórios separados por
: onde o shell busca executáveis. Quando você digita
python3, o shell percorre $PATH da esquerda para a
direita procurando um executável com esse nome. Se não encontrar:
command not found.
$HOME — diretório home do usuário atual. Igual a
~.
$USER — nome do usuário atual.
$SHELL — caminho do shell padrão do usuário.
$PWD — diretório atual (equivalente ao output de
pwd).
$EDITOR — editor preferido. Usado por
git commit, crontab -e, etc.
Para criar uma variável: VARIAVEL=valor (sem export —
só no processo atual, processos filhos não veem). Com
export VARIAVEL=valor, fica disponível para processos
filhos. Para persistir entre sessões, adicione em
~/.bashrc ou ~/.bash_profile.
# Ver o $PATH atual
$ echo $PATH
/home/usuario/.local/bin:/usr/local/bin:/usr/bin:/bin
# O shell tenta cada diretório em ordem:
# 1. /home/usuario/.local/bin/python3 ? NÃO
# 2. /usr/local/bin/python3 ? NÃO
# 3. /usr/bin/python3 ? SIM → executa
# Adicionar diretório ao PATH (persistir em ~/.bashrc)
export PATH="$HOME/.local/bin:$PATH"
# Definir editor padrão
export EDITOR=vim
# Variável local vs exportada
TESTE="local"
export TESTE_EXPORT="global"
bash -c 'echo $TESTE' # vazio — filho não vê
bash -c 'echo $TESTE_EXPORT' # global — filho vê
# Ver todas as variáveis de ambiente
$ env | sort | head -10
$ printenv HOME
# Carregar arquivo .env de um projeto
$ source .env
$ . .env # equivalente (. é alias para source)
Espaços em torno do = —
VARIAVEL = valor dá erro. Em shell, não pode ter
espaço: VARIAVEL=valor sem espaços.
Não usar aspas em valores com espaços —
NOME=João Silva vai tentar executar "Silva" como
comando. Correto: NOME="João Silva".
Modificar PATH sem o $PATH original —
export PATH=/novo/dir apaga todo o PATH existente.
Sempre faça: export PATH="/novo/dir:$PATH".
- $PATH determina onde o shell busca executáveis (da esquerda para direita)
-
export VAR=valor— disponível para processos filhos VAR=valorsem export — só no shell atual-
Persistir variáveis: adicionar em
~/.bashrc(bash) ou~/.zshrc(zsh) -
source arquivocarrega variáveis no shell atual (diferente de executar)
Pipes e Redirecionamentos
O pipe é como uma linha de montagem industrial: cada programa faz
uma coisa específica e passa o resultado para o próximo.
ps aux | grep nginx | awk '{print $2}': ps lista
todos os processos, grep filtra só os de nginx, awk extrai só os
PIDs. Três ferramentas simples, cada uma com uma função —
combinadas em algo poderoso.
Pipe | — conecta o stdout de um
processo ao stdin do próximo. Os dois processos rodam em paralelo
— não é sequencial. O kernel usa um buffer interno para transferir
os dados.
> — redireciona stdout para
arquivo (sobrescreve o conteúdo existente).
>> — redireciona stdout para
arquivo (append — adiciona ao final).
2> — redireciona stderr (file
descriptor 2) para arquivo.
2>&1 — redireciona stderr
para onde stdout está apontando. A ordem importa — deve
vir depois do redirecionamento de stdout.
&> ou
>& — redireciona stdout E
stderr juntos (bash).
< arquivo — usa arquivo como
stdin do programa.
<< EOF — heredoc: stdin
multilinha inline no script.
/dev/null — dispositivo que descarta
qualquer dado escrito nele. Use para silenciar output indesejado.
# Pipe — encontrar os 5 processos que mais usam memória
$ ps aux --sort=-%mem | head -6
# Redirecionamento de stdout
$ ls /etc > lista_etc.txt # sobrescreve
$ ls /etc >> lista_etc.txt # append
# Redirecionamento de stderr
$ ls /naoexiste 2> erros.txt # erros vão para arquivo
$ ls /etc /naoexiste &> tudo.txt # stdout e stderr juntos
# Silenciar completamente
$ comando_barulhento > /dev/null 2>&1
# stdin de arquivo
$ sort < nomes.txt
$ wc -l < arquivo.txt
# Heredoc — útil em scripts
$ cat << EOF
Linha 1
Linha 2
EOF
# Pipeline clássico: contar arquivos por extensão
$ find . -type f | sed 's/.*\.//' | sort | uniq -c | sort -rn
42 py
17 txt
8 sh
3 json
# Tee — escreve em arquivo E mostra no terminal
$ comando | tee output.txt
$ comando | tee -a output.txt # append
Ordem errada em 2>&1 —
comando 2>&1 > arquivo está errado. O
stderr vai para o terminal (stdout original), não para o arquivo.
Correto: comando > arquivo 2>&1 — primeiro
redireciona stdout para arquivo, depois stderr para onde stdout
está.
Sobrescrever arquivo com > —
ls > arquivo.txt em um arquivo existente apaga
tudo. Se quiser preservar, use >> ou habilite
set -o noclobber no bash.
|conecta stdout → stdin (paralelamente)-
>sobrescreve;>>adiciona ao final -
2>arquivoredireciona stderr;2>&1junta com stdout -
/dev/nulldescarta qualquer dado — "buraco negro" do Unix -
Ordem importa:
> arq 2>&1(correto) vs2>&1 > arq(errado)
stdin, stdout e stderr: os três file descriptors
Todo processo no Unix começa com três file descriptors (FDs) abertos — canais de comunicação numerados:
- FD 0 — stdin (standard input): entrada padrão. Por default, o teclado. O que o programa "lê" quando você não especifica entrada.
- FD 1 — stdout (standard output): saída padrão. Por default, o terminal. Onde os resultados são escritos.
- FD 2 — stderr (standard error): saída de erro. Por default, também o terminal. Onde mensagens de erro são escritas.
Programas bem escritos enviam resultados para stdout e erros para stderr. Isso é fundamental: permite que você processe resultados em um pipeline enquanto ainda vê os erros no terminal, ou que redirecione erros para um arquivo de log separado.
Todos os FDs são, internamente, ponteiros para arquivos — porque no Unix, tudo é arquivo. Você pode redirecionar qualquer FD para qualquer arquivo, dispositivo, socket ou pipe.
# Ver FDs abertos do seu shell
$ ls -la /proc/$$/fd/
lrwxrwxrwx 0 -> /dev/pts/0 # FD 0 = stdin (terminal)
lrwxrwxrwx 1 -> /dev/pts/0 # FD 1 = stdout (terminal)
lrwxrwxrwx 2 -> /dev/pts/0 # FD 2 = stderr (terminal)
# Separar stdout e stderr de um comando
$ ls /etc /naoexiste > resultados.txt 2> erros.txt
$ cat resultados.txt # arquivos de /etc
$ cat erros.txt # "ls: cannot access '/naoexiste'"
# Redirecionar stderr para stdout (útil para grep em erros)
$ make 2>&1 | grep "error:"
# Mandar mensagem de erro explicitamente para stderr em script
$ echo "ERRO: arquivo não encontrado" >&2
# Script bem comportado:
#!/bin/bash
if [ ! -f "$1" ]; then
echo "Erro: arquivo '$1' não existe" >&2 # erro -> stderr
exit 1
fi
cat "$1" # resultado -> stdout (FD 1)
- FD 0 = stdin, FD 1 = stdout, FD 2 = stderr
- Programas bem escritos: resultados → stdout, erros → stderr
-
echo "erro" >&2— escreve em stderr de dentro de scripts - Separar:
> ok.log 2> erros.log -
Juntar:
> tudo.log 2>&1ou&> tudo.log
Expansão e globbing
O shell faz várias expansões antes de executar um comando — transforma o que você digitou em algo concreto. Entender a ordem das expansões evita surpresas.
Globbing (expansão de pathname) — corresponde a nomes de arquivos usando caracteres especiais:
-
*— qualquer sequência de caracteres (exceto /) ?— exatamente um caractere qualquer[abc]— qualquer um dos caracteres listados[a-z]— qualquer caractere no range-
**— recursivo (requershopt -s globstarno bash)
Brace expansion {a,b,c} — o shell
gera strings antes de qualquer outra expansão. Não precisa de
arquivos existentes. mkdir {jan,fev,mar}_2025 cria
três diretórios.
Expansão de variável $VAR —
substitui o nome da variável pelo seu valor.
Expansão aritmética $((expr)) —
calcula expressões matemáticas.
Substituição de comando $(comando) —
executa comando e substitui pelo output.
# Globbing básico
$ ls *.py # todos os .py
$ ls arquivo?.txt # arquivo1.txt, arquivo2.txt, etc.
$ ls [Rr]eadme* # README ou readme, qualquer extensão
# Brace expansion — não precisa de arquivos existentes
$ mkdir {jan,fev,mar,abr,mai,jun}_2025
$ touch log_{error,warn,info}.txt
$ cp arquivo.txt{,.bak} # cria arquivo.txt.bak (expansão esperta!)
# Globbing recursivo (requer globstar no bash)
$ shopt -s globstar
$ ls **/*.py # todos .py em todos os subdiretórios
# Substituição de comando
$ echo "Hoje é $(date +%Y-%m-%d)"
Hoje é 2026-05-20
$ files=$(ls *.log)
$ echo "Encontrei: $files"
# Expansão aritmética
$ echo $((2 ** 10))
1024
$ echo $(($(cat /proc/sys/kernel/pid_max) / 2))
# Aspas: double quotes preservam $VAR; single quotes literal
$ echo "$HOME" # /home/usuario
$ echo '$HOME' # $HOME (literal)
Glob sem aspas em variável — se
$ARQUIVO contém espaços ou caracteres glob, usar sem
aspas pode causar word splitting e expansão indesejada. Sempre use
aspas duplas: "$ARQUIVO".
Brace expansion vs glob —
{*.py,*.js} não funciona como esperado. Brace
expansion acontece antes do glob. Para múltiplas extensões, use:
*.py *.js separados por espaço, ou
extglob.
-
*qualquer string;?um char;[abc]um dos chars -
{a,b,c}brace expansion — gera strings, não depende de arquivos -
$(comando)substituição de comando — captura output $((expr))aritmética no shell- Aspas duplas preservam variáveis; aspas simples são completamente literais
Histórico e aliases
O bash salva os comandos que você digita em
~/.bash_history (zsh usa
~/.zsh_history). O histórico é uma das ferramentas de
produtividade mais subestimadas do shell.
Navegação no histórico:
- ↑ / ↓ — navega pelo histórico
- Ctrl+R — busca reversa interativa — comece a digitar e encontra o comando mais recente que combina
history— lista o histórico com números!42— re-executa o comando número 42-
!!— re-executa o último comando (útil com sudo:sudo !!) -
!ls— re-executa o último comando que começou comls
Aliases — atalhos para comandos longos.
alias nome='comando longo'. Definidos no shell atual;
para persistir, adicione em ~/.bashrc ou
~/.zshrc.
# Histórico
$ history | tail -10
421 ls -la /etc/nginx/
422 sudo nginx -t
423 sudo systemctl reload nginx
424 tail -f /var/log/nginx/error.log
$ !422 # re-executa: sudo nginx -t
$ !! # re-executa último comando
$ sudo !! # esqueceu o sudo? re-executa com sudo
# Ctrl+R (busca reversa)
# Digite: Ctrl+R → "nginx" → mostra último comando com "nginx"
# Enter para executar, Ctrl+R de novo para buscar anterior
# Ctrl+C para cancelar
# Aliases úteis
$ alias ll='ls -la --color=auto'
$ alias gs='git status'
$ alias gp='git push'
$ alias cls='clear'
$ alias ..='cd ..'
$ alias ...='cd ../..'
$ alias grep='grep --color=auto'
# Ver aliases definidos
$ alias
alias ll='ls -la --color=auto'
alias gs='git status'
# Remover alias
$ unalias gs
# Adicionar em ~/.bashrc para persistir:
# echo "alias ll='ls -la --color=auto'" >> ~/.bashrc
# source ~/.bashrc # recarregar
Para aliases mais complexos que precisam de argumentos, use funções no bash:
# Alias não aceita argumentos facilmente — use função
mkcd() {
mkdir -p "$1" && cd "$1"
}
# mkcd novo_projeto → cria e já entra no diretório
- Ctrl+R — busca no histórico (mais útil que ↑↑↑)
-
!!re-executa último;!Nre-executa número N;!stringre-executa último que começa com string alias nome='comando'— atalhos de comandos-
Persistir aliases: adicionar em
~/.bashrce recarregar comsource ~/.bashrc - Para aliases com argumentos, use funções bash
Terminal Simulado — Pratique aqui
ℹ️ Terminal simulado para prática básica — use um terminal real para comandos avançados.
Tente: ls, ls -la, pwd,
cd documentos, cat notas.txt,
echo $HOME, whoami, date,
uname -a, history, clear
Use ↑ / ↓ para navegar no histórico de comandos.