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:

  1. Divide a linha em palavras nos espaços em branco que não estão entre aspas.
  2. Faz as expansões: ~ vira o seu diretório home, $VAR é substituído pelo valor, $(cmd) pela saída de um comando, {a,b} por a b, e padrões glob como *.log pelos nomes de arquivo correspondentes.
  3. Configura os redirecionamentos (>, <, |) e os remove da lista de argumentos.
  4. 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 do PATH, nessa ordem.
  5. 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.

SintaxeEfeito
cmd > filestdout para file, truncando o arquivo antes
cmd >> filestdout acrescentado ao final de file
cmd 2> filestderr para file
cmd > file 2>&1 ou cmd &> fileos dois fluxos para file
cmd 2> /dev/nulldescartar os erros
cmd < filestdin lido de file
cmd <<< "text"stdin a partir de uma string (uma "here string")
cmd1 | cmd2o 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-R e depois digite — busca incremental para trás; aperte Ctrl-R de 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 shellArquivos lidosExemplo
Interativo de login/etc/profile e depois o primeiro que existir entre ~/.bash_profile, ~/.bash_login, ~/.profileSessão SSH, login no console
Interativo sem login/etc/bash.bashrc (Debian) ou /etc/bashrc (RHEL) e depois ~/.bashrcNova janela de terminal, bash digitado no prompt
Não interativoNenhum, a menos que BASH_ENV esteja definidaScripts, 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; type revela 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.
  • export leva 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.