Ubuntu Pipes e xargs

O pipe (|, "cano" em inglês) liga a saída de um comando à entrada do próximo. Com ele, comandos pequenos viram uma linha de montagem: um lista, outro filtra, outro conta. É uma das ideias mais poderosas do Linux.

Você vai aprender
  • Como o | conecta dois comandos, e a diferença entre | e >.
  • A montar um pipe de várias etapas uma etapa por vez, conferindo cada resultado.
  • A ver e gravar ao mesmo tempo com tee.
  • A transformar linhas em argumentos com xargs, para comandos que não leem a entrada.

A ideia: saída de um vira entrada do outro

Na aula de Redirecionamento você viu que todo comando tem uma entrada (canal 0) e uma saída (canal 1). O pipe encaixa os dois:

  comando1  ── saída ──►  |  ── entrada ──►  comando2  ──►  tela

O comando1 não mostra nada na tela. Tudo o que ele produziria vai direto para o comando2, que trabalha em cima disso.

Seu primeiro pipe

Quantos arquivos há na pasta Downloads? O ls lista, e o wc -l conta linhas.

ls Downloads
Lista os arquivos, um por linha (quando a saída não é a tela).
|
O pipe: entrega essa lista ao próximo comando.
wc -l
Conta as linhas que recebeu (word count, opção -l de lines).
$ ls Downloads
foto.jpg  fotos-viagem.zip  manual.pdf  musica.mp3  ubuntu-26.04-desktop-amd64.iso
$ ls Downloads | wc -l
5                                « 5 linhas = 5 arquivos

O símbolo | fica, na maioria dos teclados brasileiros, na tecla ao lado do Z ou do Backspace, junto com a barra invertida: aperte Shift + essa tecla.


Montando um pipe passo a passo

Ninguém escreve um pipe longo de uma vez. O jeito profissional é acrescentar uma etapa, rodar, olhar o resultado, e só então acrescentar a próxima. Vamos responder: qual fruta aparece mais vezes em frutas.txt?

  1. Veja os dados brutos.

    $ cat frutas.txt
    banana
    maçã
    laranja
    banana
    uva
    maçã
    banana
  2. Ordene, para que as frutas iguais fiquem juntas.

    $ sort frutas.txt
    banana
    banana
    banana
    laranja
    maçã
    maçã
    uva
  3. Conte as repetições. O uniq -c junta linhas iguais vizinhas e escreve quantas eram. Por isso o sort veio antes.

    $ sort frutas.txt | uniq -c
          3 banana
          1 laranja
          2 maçã
          1 uva
  4. Ordene pela contagem, do maior para o menor: -n compara como número, -r inverte.

    $ sort frutas.txt | uniq -c | sort -nr
          3 banana
          2 maçã
          1 uva
          1 laranja
  5. Fique só com a primeira linha.

    $ sort frutas.txt | uniq -c | sort -nr | head -n 1
          3 banana                   « a resposta
sort frutas.txt
Ordena, juntando as linhas iguais.
| uniq -c
Conta cada grupo de linhas iguais.
| sort -nr
Ordena pelo número, do maior para o menor.
| head -n 1
Mostra só a primeira linha.

Esse mesmo "molde" responde muitas perguntas do dia a dia: quais IPs mais acessaram um site, quais comandos você mais usa, quais extensões de arquivo dominam uma pasta.

Missão: monte o pipe acima etapa por etapa, rodando cada versão. No fim, grave o ranking completo (sem o head) no arquivo ranking.txt. Dica: use as setas ↑ para trazer o comando anterior e só acrescentar a próxima etapa.

Outro exemplo: quem mais acessou o site?

O arquivo Documentos/acesso.log tem uma linha por acesso, e o primeiro "campo" é o IP do visitante:

$ head -n 2 Documentos/acesso.log
192.168.0.10 - - [15/Oct/2026:09:01:12] "GET / HTTP/1.1" 200
192.168.0.23 - - [15/Oct/2026:09:01:40] "GET /contato HTTP/1.1" 200
$ cut -d " " -f 1 Documentos/acesso.log | sort | uniq -c | sort -nr
      3 192.168.0.10
      2 192.168.0.23
      1 192.168.0.57

O cut -d " " -f 1 corta cada linha nos espaços (-d, de delimiter) e fica com o campo 1 (-f, de field). O resto é o mesmo molde. Veja mais sobre cut em sort, uniq, cut, wc, tr.

Missão: (1) descubra qual IP mais acessou, montando o pipe aos poucos. (2) Use grep para mostrar só as linhas do log com o código 404 (página não encontrada).


Filtrar a saída de qualquer comando

O grep é o parceiro mais comum do pipe: ele deixa passar só as linhas que contêm um texto.

$ ls -l Documentos | grep txt
-rw-rw-r-- 1 ana ana   71 Oct 14 17:40 notas.txt
$ history | grep mkdir
   12  mkdir jan fev mar
   15  mkdir -p loja/{src,docs}

Cuidado: o grep encontra a si mesmo

Um uso clássico é procurar um programa na lista de processos. Repare na última linha:

$ ps aux | grep chrome
ana         2210  5.4  6.8 1100800 1093000 ?     Sl   08:00   0:16 /snap/chromium/3554/usr/lib/chromium-browser/chrome --password-store
ana         2251  2.0  3.9 636000 629000 ?       Sl   08:00   0:06 /snap/chromium/3554/usr/lib/chromium-browser/chrome --type=renderer
ana         3150  0.0  0.0   9144  2304 pts/0    S+   10:30   0:00 grep --color=auto chrome    « o próprio grep

O grep chrome também é um processo, e a linha de comando dele contém "chrome". Duas soluções:

  • pgrep -a chrome, que foi feito para isso (veja ps, pgrep, top);
  • ps aux | grep "[c]hrome". O padrão [c]hrome casa com "chrome", mas o texto do próprio grep agora é "[c]hrome", que não casa.

Ver e gravar ao mesmo tempo: tee

Com >, a saída vai para o arquivo e some da tela. O tee (nome de uma conexão hidráulica em forma de "T") divide o fluxo em dois: grava num arquivo e passa adiante.

  comando  ──►  tee arquivo.txt  ──►  tela (ou próximo comando)
                     │
                     ▼
                arquivo.txt
ls Documentos
O comando.
| tee lista.txt
Grava em lista.txt e repete na tela.
$ ls Documentos | tee lista.txt
acesso.log
notas.txt
projetos
relatorio.odt
$ cat lista.txt
acesso.log
notas.txt
projetos
relatorio.odt

Com -a (de append), o tee acrescenta em vez de substituir, como o >>.

O uso mais importante: escrever em arquivos do sistema

Como você viu em Redirecionamento, sudo echo … > /etc/arquivo falha. Com tee, quem abre o arquivo é o próprio tee, e ele pode rodar com sudo:

$ echo "vm.swappiness=10" | sudo tee /etc/sysctl.d/99-swap.conf
[sudo: authenticate] Senha:
vm.swappiness=10
$ echo "vm.swappiness=10" | tee /etc/sysctl.d/99-swap.conf
/etc/sysctl.d/99-swap.conf: Permission denied (os error 13)        « sem sudo: negado

Se você não quer o eco na tela, descarte-o: … | sudo tee arquivo > /dev/null.


xargs: transformar linhas em argumentos

Alguns comandos leem a entrada (sort, grep, wc). Outros não: mkdir, rm, cp e touch só aceitam nomes como argumentos. Mandar algo para eles por pipe não funciona:

$ cat pastas.txt
fotos
videos
musicas
$ cat pastas.txt | mkdir
erro: os seguintes argumentos obrigatórios não foram fornecidos:
  <dirs>...                                « faltou o nome das pastas

Usage: mkdir [OPÇÃO]... DIRETÓRIO...

Para obter mais informações, use '--help'.

O mkdir recebeu a lista pela entrada, mas não lê a entrada. Para ele, foi como digitar só mkdir, sem nome nenhum.

O xargs resolve isso: ele lê as linhas e as coloca no fim do comando, como se você tivesse digitado.

cat pastas.txt
Produz os nomes, um por linha.
| xargs
Lê esses nomes da entrada…
mkdir
…e roda mkdir fotos videos musicas.
$ cat pastas.txt | xargs mkdir
$ ls -d fotos videos musicas
fotos  musicas  videos

Ver o comando que o xargs montou: -t

A opção -t mostra o comando antes de rodar. É ótima para aprender e para conferir:

$ cat pastas.txt | xargs -t mkdir -p
mkdir -p fotos videos musicas

Um item por vez: -n 1 e -I {}

Por padrão, o xargs coloca todos os itens num comando só. Com -n 1, roda uma vez para cada item. Com -I {}, você escolhe onde o item entra no comando:

$ cat pastas.txt | xargs -n 1 echo "criando:"
criando: fotos
criando: videos
criando: musicas
$ cat pastas.txt | xargs -I {} mkdir {}-2026
$ ls -d *-2026
fotos-2026  musicas-2026  videos-2026

Nomes com espaço: -d '\n' e -0

O xargs separa os itens por espaços. Um nome como "Meus Projetos" vira dois:

$ echo "Meus Projetos" | xargs mkdir
$ ls
Meus  Projetos                   « duas pastas, não uma

Use xargs -d '\n' para separar só por linha. Com o find, o par mais seguro é find … -print0 | xargs -0, que usa um separador invisível que nunca aparece em nomes:

$ find . -name "*.tmp" -print0 | xargs -0 rm -v
removido './cache 1.tmp'
removido './cache 2.tmp'
Teste antes de apagar

Antes de qualquer | xargs rm, rode o pipe sem o rm (ou com xargs -t echo rm) e confira a lista. Um padrão errado no find apaga arquivos que você não queria.

Missão: o arquivo pastas.txt já tem três nomes. Veja o conteúdo com cat e crie as três pastas usando xargs. Experimente também o -t para ver o comando montado.


O que um pipe devolve

O código de saída ($?) de um pipe é o do último comando. Os erros das etapas anteriores ficam escondidos:

$ cat nada.txt | wc -l
cat: nada.txt: No such file or directory
0
$ echo $?
0                                « o wc deu certo, então o pipe "deu certo"

Em scripts, ligue set -o pipefail: assim, se qualquer etapa falhar, o pipe inteiro devolve erro.

$ set -o pipefail
$ cat nada.txt | wc -l
cat: nada.txt: No such file or directory
0
$ echo $?
1

Repare também que a mensagem do cat apareceu na tela, e não foi para o wc. O pipe só leva o canal 1 (saída). Para levar os erros junto, use 2>&1 | ou o atalho |&.


Formas diferentes de contar linhas

FormaExemploObservação
Arquivo como argumentowc -l frutas.txtMostra o número e o nome.
Redirecionamentowc -l < frutas.txtSó o número; ótimo em scripts.
Pipe com catcat frutas.txt | wc -lFunciona, mas o cat é desnecessário.
Saída de outro comandols Downloads | wc -lAqui o pipe é necessário: os dados vêm de um comando.
grepgrep -c banana frutas.txtConta só as linhas que casam.

Quando não funciona

Apareceu um arquivo chamado "wc"
$ ls > wc
$ ls
Documentos  Downloads  …  wc

O que aconteceu: foi usado > no lugar de |. O > manda a saída para um arquivo; o | manda para um comando.

Como resolver: apague o arquivo (rm wc) e use ls | wc -l.

"falta operando" depois do pipe
$ find . -name "*.tmp" | rm
rm: falta operando
Tente "rm --help" para mais informações.

O que aconteceu: o rm não lê a entrada; ele espera nomes como argumentos.

Como resolver: coloque o xargs no meio, find . -name "*.tmp" -print0 | xargs -0 rm, ou use o próprio find . -name "*.tmp" -delete.

O pipe não mostrou nada
$ ps aux | grep firefox
ana         3170  0.0  0.0   9144  2304 pts/0    S+   10:31   0:00 grep --color=auto firefox

O que aconteceu: a única linha é o próprio grep. O Firefox não está aberto, ou o processo tem outro nome. No caso do Chromium instalado pelo Snap, o processo se chama chrome.

Como resolver: rode cada etapa sozinha. Primeiro ps aux, depois procure com outro termo, como ps aux | grep -i chrom. Veja o caso completo em ps, pgrep, top.

Nome com espaço virou dois itens

Se o xargs criou "Meus" e "Projetos" separados, use xargs -d '\n' ou, com o find, -print0 | xargs -0.


Referência

RecursoO que fazExemplo
a | bSaída de a vira entrada de bls | wc -l
a |& bSaída e erros de a para bmake |& less
tee arqGrava e repassals | tee lista.txt
tee -a arqAcrescenta e repassadate | tee -a log.txt
| sudo tee arqGrava em arquivo do rootecho x | sudo tee /etc/a.conf
xargs cmdLinhas da entrada viram argumentoscat lista | xargs mkdir
xargs -tMostra o comando antes de rodarxargs -t rm
xargs -n 1Um item por execuçãoxargs -n 1 echo
xargs -I {}Escolhe onde o item entraxargs -I {} cp {} {}.bak
xargs -0Itens separados pelo caractere nulofind -print0 | xargs -0
xargs -d '\n'Itens separados por linhaxargs -d '\n' mkdir
set -o pipefailPipe falha se qualquer etapa falharUse em scripts

Exercícios

1. Contar arquivos

Complete para contar quantos itens há na pasta Imagens:

ls Imagens |

2. O molde da contagem

Complete o pipe que conta quantas vezes cada linha aparece:

sort frutas.txt | | sort -nr

3. Gravar como root

Complete para gravar a linha no arquivo do sistema:

echo "nameserver 1.1.1.1" | /etc/resolv-teste.conf

4. Por que cat pastas.txt | mkdir não funciona?

5. Em sort frutas.txt | uniq -c, por que o sort vem antes?


Resumo

  • | liga a saída de um comando à entrada do próximo; > liga a um arquivo.
  • Monte pipes longos uma etapa por vez, olhando o resultado de cada uma.
  • tee grava e mostra ao mesmo tempo; | sudo tee escreve em arquivos do sistema.
  • xargs transforma linhas em argumentos para comandos como mkdir e rm.
  • O pipe só leva a saída (canal 1), e o código de saída é o do último comando.