Administração Linux
Fundamentos do shell Bash: comandos, redirecionamento, pipes, variáveis e configuração
Iniciante10 min de leituraAtualizado
Esta página foi traduzida do inglês com a ajuda de IA e conferida com o original. Ler a versão em inglês
Este guia explica o que realmente acontece entre apertar Enter e um programa começar a rodar: a busca do comando, as aspas, as expansões e os três fluxos padrão. Você vai aprender a redirecionar a saída e os erros, montar pipelines, usar o histórico e os atalhos de edição que deixam o shell rápido, gerenciar variáveis de ambiente e o PATH, e configurar o Bash pelo .bashrc e pelo .profile sem quebrar o seu login.
O que você vai aprender
- Explicar como o Bash interpreta uma linha de comando e localiza o programa a executar
- Redirecionar a saída padrão e o erro padrão para arquivos e conectar comandos com pipes
- Usar aspas e globbing corretamente para que espaços e caracteres especiais não causem surpresas
- Definir, exportar e inspecionar variáveis do shell e de ambiente, incluindo o PATH
- Configurar aliases e ambiente no arquivo de inicialização certo e usar as páginas man com eficiência
Um shell é um programa que lê linhas de texto, descobre quais programas executar e com quais argumentos, executa esses programas e mostra o resultado. O Bash (o "Bourne-Again SHell") é o shell interativo padrão de quase todas as distribuições Linux e o interpretador que a maioria dos scripts de administração ainda pressupõe. Entender o que o Bash faz com uma linha de comando antes de executar qualquer coisa — expansões, aspas, redirecionamentos — é o que separa quem digita comandos de quem consegue prever o que um comando vai fazer.
O que acontece quando você aperta Enter
Pegue uma linha como grep -i error /var/log/*.log > errors.txt. O Bash a processa em etapas:
- Divide a linha em palavras nos espaços em branco que não estão entre aspas.
- Faz as expansões:
~vira o seu diretório home,$VARé substituído pelo valor,$(cmd)pela saída de um comando,{a,b}pora b, e padrões glob como*.logpelos nomes de arquivo correspondentes. - Configura os redirecionamentos (
>,<,|) e os remove da lista de argumentos. - Resolve a primeira palavra: um alias, uma função do shell, um builtin (
cd,echo,export…) ou um programa externo encontrado percorrendo os diretórios doPATH, nessa ordem. - Cria um processo filho (fork), aplica nele os redirecionamentos e executa o programa com as palavras restantes como argumentos.
O programa nunca vê o *, o > nem as aspas; ele recebe uma lista de strings. O grep não sabe que *.log era um padrão — ele simplesmente recebe /var/log/syslog /var/log/kern.log …. É por isso que as aspas importam tanto, e é por isso que todo programa no Linux trata curingas do mesmo jeito: nenhum deles trata, quem trata é o shell.
type informa em qual categoria um nome se encaixa, e command -v mostra o caminho de um comando externo:
$ type cd ls ll
cd is a shell builtin
ls is /usr/bin/ls
ll is aliased to `ls -alF'
$ echo "$PATH"
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
O PATH é percorrido da esquerda para a direita, e a primeira ocorrência vence. O diretório atual propositalmente não está nele, então um programa no seu diretório de trabalho precisa ser executado como ./program — isso impede que um ls malicioso deixado em /tmp sequestre seus comandos.
Anatomia de um comando
A maioria dos comandos segue o formato command [options] [arguments]. As opções existem na forma curta (-l), curta agrupada (-la = -l -a) e longa (--all); algumas recebem um valor (-n 5, --lines=5). Os argumentos geralmente são nomes de arquivo. Um -- sozinho marca o fim das opções, então rm -- -rf apaga um arquivo chamado literalmente -rf.
O manual já vem embutido. man ls abre a página do ls; dentro dela, /pattern faz uma busca, n pula para a próxima ocorrência e q sai. As páginas são agrupadas em seções numeradas — 1 para comandos, 5 para formatos de arquivo, 8 para administração — e o número desfaz ambiguidades: man 5 passwd é o formato de arquivo, man 1 passwd é o comando. man -k keyword (ou apropos) busca nos resumos das páginas quando você não sabe o nome do comando. A maioria dos programas também mostra um resumo com --help.
Os três fluxos padrão
Todo processo começa com três descritores de arquivo abertos: a entrada padrão (fd 0), a saída padrão (fd 1) e o erro padrão (fd 2). Em um terminal, os três apontam para o terminal, e é por isso que a saída e as mensagens de erro aparecem misturadas. O redirecionamento faz com que apontem para outro lugar antes de o programa começar.
| Sintaxe | Efeito |
|---|---|
cmd > file | stdout para file, truncando o arquivo antes |
cmd >> file | stdout acrescentado ao final de file |
cmd 2> file | stderr para file |
cmd > file 2>&1 ou cmd &> file | os dois fluxos para file |
cmd 2> /dev/null | descartar os erros |
cmd < file | stdin lido de file |
cmd <<< "text" | stdin a partir de uma string (uma "here string") |
cmd1 | cmd2 | o stdout de cmd1 vira o stdin de cmd2 |
Como o programa só enxerga descritores, ele não tem como saber se está escrevendo em um terminal, em um arquivo ou em outro processo. Essa decisão de design é o que torna os pipelines possíveis: programas pequenos que leem do stdin e escrevem no stdout podem ser encadeados à vontade.
# Contar os endereços IP distintos que acessaram o servidor, os mais ativos primeiro
cut -d' ' -f1 /var/log/apache2/access.log | sort | uniq -c | sort -rn | head
O tee copia a entrada para um arquivo e para o stdout, então você guarda um registro enquanto continua acompanhando: ./build.sh 2>&1 | tee build.log. O xargs transforma linhas de entrada em argumentos para outro comando: find . -name '*.tmp' -print0 | xargs -0 rm (o par -print0/-0 preserva nomes de arquivo com espaços).
Dois builtins completam o quadro. set -o noclobber faz o > se recusar a sobrescrever um arquivo existente (>| força a escrita). E exec > logfile redireciona o stdout do próprio shell pelo resto de um script.
Aspas e expansão
As aspas controlam quais expansões acontecem.
- Aspas simples
'…'suprimem tudo.'$HOME'são literalmente os cinco caracteres$HOME. - Aspas duplas
"…"suprimem o globbing e a divisão em palavras, mas continuam fazendo as expansões$var,$(cmd)e$((math)). - Uma barra invertida escapa apenas o caractere seguinte.
A consequência prática: um $file sem aspas contendo my report.txt vira dois argumentos depois da divisão em palavras. "$file" continua sendo um só. Crie o hábito de colocar aspas duplas em toda expansão de variável, a menos que você queira justamente a divisão.
name="Ada Lovelace"
echo Hello $name # echo recebe 3 argumentos: Hello, Ada, Lovelace
echo "Hello $name" # echo recebe 1 argumento
echo 'Hello $name' # imprime Hello $name
echo "Today is $(date +%A)"
echo "Free memory: $(( $(grep MemAvailable /proc/meminfo | tr -s ' ' | cut -d' ' -f2) / 1024 )) MB"
Padrões glob casam com nomes de arquivo: * qualquer sequência de caracteres, ? um caractere, [a-z] um caractere de um conjunto, [!0-9] um caractere fora do conjunto. shopt -s globstar habilita ** para correspondência recursiva (**/*.java). Uma peculiaridade importante: por padrão, quando um padrão não casa com nada, o Bash o repassa literalmente, então rm *.bak em um diretório sem arquivos .bak tenta apagar um arquivo chamado *.bak e falha. shopt -s nullglob faz padrões sem correspondência se expandirem para nada, que geralmente é o que os scripts querem.
A expansão de chaves não é globbing — ela gera strings independentemente de os arquivos existirem: mkdir -p project/{src,test,docs}, cp config.yml{,.bak}, echo {01..12}.
Variáveis e o ambiente
Atribua sem espaços em volta do =: retries=3. Leia com $retries ou, de forma mais segura, ${retries}. O Bash oferece muitas variáveis automáticas: $? é o status de saída do último comando, $$ o PID do shell, além de $HOME, $USER, $PWD, $RANDOM e $SECONDS.
Uma atribuição simples cria uma variável do shell, privada deste processo. export a promove para o ambiente, o bloco de strings NAME=value entregue a cada processo filho. env e printenv listam o ambiente; set lista tudo, incluindo variáveis do shell e funções.
JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
export JAVA_HOME # agora visível para os programas que você iniciar
export PATH="$JAVA_HOME/bin:$PATH" # no início: encontrado antes do java do sistema
LANG=C sort names.txt # pontual: só para este comando
unset JAVA_HOME
A expansão de parâmetros oferece valores padrão e transformações que economizam muito código:
echo "${EDITOR:-nano}" # o valor, ou "nano" se não definida/vazia
: "${DB_HOST:?must be set}" # aborta com uma mensagem se não definida
f=/var/log/app/server.log
echo "${f##*/}" # server.log (remove o maior prefixo que casa com */)
echo "${f%.log}" # /var/log/app/server (remove o sufixo)
echo "${#f}" # tamanho da string
Status de saída e encadeamento de comandos
Todo comando termina com um status de saída inteiro: 0 significa sucesso, qualquer outro valor significa falha (o significado de cada valor diferente de zero é definido pelo programa). O Bash o expõe como $? e o usa no controle de fluxo. Três operadores encadeiam comandos:
make && sudo make install # instala só se o build deu certo
ping -c1 db01 || echo "db01 unreachable"
cd /tmp; ls # executa os dois em qualquer caso
O grep retorna 1 quando não encontra nada, então grep -q pattern file && do_something é idiomático. true e false existem apenas para produzir códigos de saída.
Trabalhando mais rápido: histórico e edição de linha
O Bash mantém um histórico de comandos (history, salvo em ~/.bash_history ao sair). Recuperá-los é mais rápido do que digitar de novo:
Ctrl-Re depois digite — busca incremental para trás; aperteCtrl-Rde novo para ocorrências mais antigas, Enter para executar, Esc para editar.!!repete o último comando (sudo !!é o uso clássico);!$é o último argumento do comando anterior.Alt-.insere interativamente o último argumento do comando anterior.
O editor de linha usa por padrão os atalhos do Emacs: Ctrl-A/Ctrl-E vão para o início/fim, Ctrl-W apaga a palavra anterior, Ctrl-U apaga até o início da linha, Ctrl-K até o fim, Ctrl-L limpa a tela, e Tab completa nomes de arquivo, nomes de comando e — com o pacote bash-completion — opções e argumentos de centenas de ferramentas. Aperte Tab duas vezes para listar os candidatos.
Ctrl-C interrompe o comando em execução; Ctrl-D em um prompt vazio envia fim de arquivo e encerra a sessão; Ctrl-Z suspende o job em primeiro plano (abordado junto com o resto do controle de jobs em Processos e systemd).
Aliases, funções e arquivos de inicialização
Um alias é um atalho textual expandido quando aparece como primeira palavra de um comando: alias ll='ls -alF', alias gs='git status'. Aliases não aceitam argumentos no meio; para isso, defina uma função:
mkcd() { mkdir -p -- "$1" && cd -- "$1"; }
O Bash lê arquivos diferentes dependendo de como foi iniciado, e é por isso que "coloquei no .bashrc e não funciona" é uma reclamação tão frequente.
| Tipo de shell | Arquivos lidos | Exemplo |
|---|---|---|
| Interativo de login | /etc/profile e depois o primeiro que existir entre ~/.bash_profile, ~/.bash_login, ~/.profile | Sessão SSH, login no console |
| Interativo sem login | /etc/bash.bashrc (Debian) ou /etc/bashrc (RHEL) e depois ~/.bashrc | Nova janela de terminal, bash digitado no prompt |
| Não interativo | Nenhum, a menos que BASH_ENV esteja definida | Scripts, cron, ssh host command |
A organização convencional: coloque as variáveis de ambiente (PATH, JAVA_HOME, EDITOR) no ~/.profile, para que sejam definidas uma única vez no login e herdadas por todo o resto, e coloque aliases, funções, configurações do prompt e opções do shopt no ~/.bashrc, porque eles precisam ser redefinidos em cada shell interativo. O ~/.profile padrão do Debian e do Ubuntu carrega o ~/.bashrc, então os dois valem para logins via SSH. Configurações globais para todos os usuários vão em um novo arquivo dentro de /etc/profile.d/.
Depois de editar, aplique as mudanças ao shell atual com source ~/.bashrc (ou . ~/.bashrc) — rodar bash ~/.bashrc executaria o arquivo em um processo filho que termina em seguida, sem mudar nada.
Obtendo ajuda sem sair do terminal
Além de man e --help: help cd documenta um builtin (a página man do Bash tem mais de 100 páginas; o help vai direto ao ponto); info coreutils traz os manuais GNU completos; whatis cmd mostra o resumo de uma linha; e /usr/share/doc/<package>/ guarda READMEs, exemplos e changelogs instalados com cada pacote. Quando a saída de um comando for longa, passe-a por um pipe para o less e busque com /.
Erros comuns
Espaços em volta do =. count = 3 executa um comando chamado count com dois argumentos. Atribuições não têm espaços.
Variáveis sem aspas contendo espaços ou globs. rm $file com file="report *.txt" apaga todos os arquivos .txt. Coloque aspas.
Esperar que o .bashrc afete o cron, o systemd ou scripts. Shells não interativos não o leem. Defina o PATH e as demais variáveis no script ou na unit.
Ordem dos redirecionamentos. cmd 2>&1 > file continua mostrando os erros no terminal. Coloque > file primeiro ou use &> file.
Rodar com sh scripts escritos para o Bash. sh script.sh usa o Dash no Debian e no Ubuntu e falha em [[, arrays e local -n. Use o shebang e chmod +x, e então rode ./script.sh.
Usar cat file | grep x. Não faz mal, mas grep x file é mais simples e deixa o grep mostrar os nomes de arquivo quando recebe vários. Reserve o cat para concatenar.
Sobrescrever o arquivo que você está lendo. sort names.txt > names.txt trunca o arquivo antes mesmo que o sort o leia, deixando-o vazio. Escreva em um arquivo temporário e renomeie, ou use o sponge do moreutils.
Principais lições
- O Bash expande a linha de comando — variáveis, globs, chaves, substituição de comandos — antes de o programa rodar; os programas recebem simples strings como argumentos.
- A ordem de busca de um comando é alias, função, builtin e, por fim, a busca no
PATH;typerevela qual se aplica. - Os três fluxos padrão e os operadores de redirecionamento (
>,>>,2>,2>&1,<,|) são o que torna ferramentas pequenas combináveis. - Coloque aspas duplas nas expansões de variáveis e aspas simples em texto literal com
$ou barras invertidas. exportleva uma variável para o ambiente dos processos filhos; variáveis não exportadas ficam no shell.- O ambiente vai no
~/.profile, e aliases e configurações do prompt no~/.bashrc; shells não interativos não leem nenhum dos dois. - Status de saída 0 significa sucesso, e
&&,||e$?permitem agir com base nele.
Fontes e leituras complementares
Perguntas frequentes
- Qual a diferença entre uma variável do shell e uma variável de ambiente?
- Uma variável do shell existe apenas dentro do processo do shell atual. Exportá-la (export NOME=valor) a copia para o bloco de ambiente entregue a cada processo filho que o shell inicia. Programas como java, python ou curl só enxergam variáveis exportadas. Variáveis definidas em um script nunca ficam visíveis para o shell pai, porque um processo filho não pode alterar a memória do pai.
- Por que meu alias ou minha alteração no PATH funciona no terminal, mas não no cron nem em um serviço do systemd?
- Aliases e o .bashrc só são lidos por shells interativos. Jobs do cron e serviços rodam sem terminal e com um ambiente mínimo, então não enxergam nem os seus aliases nem o que você acrescentou ao PATH no .bashrc. Use caminhos absolutos nos scripts, defina o PATH explicitamente no início do script ou declare o ambiente na própria unit do serviço ou na crontab.
- Quando usar aspas simples e quando usar aspas duplas?
- Aspas simples são literais: nada dentro delas é expandido, então são a escolha certa para expressões regulares, programas awk e qualquer texto com $ ou barras invertidas que você queira preservar. Aspas duplas mantêm os espaços juntos, mas continuam expandindo $variáveis e $(comandos), que é o que você quer ao passar uma variável como um único argumento. Regra prática: coloque aspas duplas em toda referência a variável e aspas simples em texto fixo com caracteres especiais.
- O que significa 2>&1 e por que a ordem importa?
- 2>&1 faz o descritor 2 (erro padrão) virar uma cópia de para onde o descritor 1 (saída padrão) aponta naquele momento. Os redirecionamentos são aplicados da esquerda para a direita, então cmd > log 2>&1 manda os dois fluxos para o arquivo, enquanto cmd 2>&1 > log primeiro copia o stderr para o terminal e só depois aponta o stdout para o arquivo. O Bash também aceita a forma abreviada &> log.