Pipe é usado para passar a saída para outro programa ou utilitário .
O redirecionamento é usado para passar a saída para um arquivo ou fluxo .
Exemplo: thing1 > thing2
vsthing1 | thing2
thing1 > thing2
- Seu shell executará o programa chamado
thing1
- Tudo o que
thing1
sai será colocado em um arquivo chamado thing2
. (Nota - se thing2
existir, será substituído)
Se você deseja passar a saída do programa thing1
para um programa chamado thing2
, você pode fazer o seguinte:
thing1 > temp_file && thing2 < temp_file
qual seria
- executar o programa chamado
thing1
- salve a saída em um arquivo chamado
temp_file
- execute o programa nomeado
thing2
, fingindo que a pessoa no teclado digitou o conteúdo temp_file
como entrada.
No entanto, isso é desajeitado, então eles fizeram tubos como uma maneira mais simples de fazer isso. thing1 | thing2
faz a mesma coisa quething1 > temp_file && thing2 < temp_file
EDIT para fornecer mais detalhes para questionar no comentário:
Se você >
tentar "passar para o programa" e "gravar no arquivo", isso poderá causar problemas nas duas direções.
Primeiro exemplo: você está tentando gravar em um arquivo. Já existe um arquivo com esse nome que você deseja sobrescrever. No entanto, o arquivo é executável. Presumivelmente, ele tentaria executar esse arquivo, passando a entrada. Você teria que fazer algo como gravar a saída em um novo nome de arquivo e renomear o arquivo.
Segundo exemplo: como apontou Florian Diesch, e se houver outro comando em outro lugar do sistema com o mesmo nome (que está no caminho de execução). Se você pretendia criar um arquivo com esse nome na sua pasta atual, ficaria preso.
Terceiro: se você digitar incorretamente um comando, ele não avisará que o comando não existe. Agora, se você digitar, ls | gerp log.txt
ele informará bash: gerp: command not found
. Se os >
dois significassem, simplesmente criaria um novo arquivo para você (avise que não sabe o que fazer log.txt
).
thing1 > temp_file && thing2 < temp_file
que é mais fácil usar canos. Mas por que não reutilizar o>
operador para fazer isso, por exemplo,thing1 > thing2
para comandosthing1
ething2
? Por que um operador extra|
?less
, por exemplo?thing | less
ething > less
são perfeitamente diferentes, pois fazem coisas diferentes. O que você propõe criaria uma ambiguidade.tee
comando faz algo diferente.tee
grava a saída na tela (stdout
) e no arquivo. Redirecionar faz apenas o arquivo.Se o significado de
foo > bar
depender de se existe um comando nomeadobar
que tornaria o redirecionamento muito mais difícil e mais propenso a erros: toda vez que eu quero redirecionar para um arquivo, primeiro eu tenho que verificar se existe um comando chamado como meu arquivo de destino.fonte
bar
em um diretório que faça parte da sua$PATH
variável env. Se você estiver em algo como / bin, ot pode ser um problema. Mas, mesmo assim,bar
teria que ter uma permissão de executável definida, para que o shell verifique não apenas a localização de um executável,bar
mas também possa executá-lo. E se a preocupação for sobrescrever arquivo existente, anoclober
opção shell deve impedir a substituição de arquivos existentes nos redirecionamentos.No Manual de Administração de Sistemas Unix e Linux:
Então, minha interpretação é: se for comando a comando, use um pipe. Se você estiver produzindo para ou de um arquivo, use o redirecionamento.
fonte
Há uma diferença vital entre os dois operadores:
ls > log.txt
-> Este comando envia a saída para o arquivo log.txt.ls | grep file.txt
-> Este comando envia a saída do comando ls to grep através do uso de pipe (|
), e o comando grep procura por file.txt na entrada fornecida pelo comando anterior.Se você tivesse que executar a mesma tarefa usando o primeiro cenário, seria:
Portanto, um canal (com
|
) é usado para enviar a saída para outro comando, enquanto o redirecionamento (com>
) é usado para redirecionar a saída para algum arquivo.fonte
Há uma grande diferença sintática entre os dois:
Você pode pensar de redirecionamentos como este:
cat [<infile] [>outfile]
. Isso implica que a ordem não importa:cat <infile >outfile
é o mesmo quecat >outfile <infile
. Você pode até misturar redirecionamentos com outros argumentos:cat >outfile <infile -b
ecat <infile -b >outfile
ambos estão perfeitamente bem. Também você pode encadear mais de uma entrada ou saída (entradas serão lidos sequencialmente e todas as saídas serão gravados em cada arquivo de saída):cat >outfile1 >outfile2 <infile1 <infile2
. O destino ou a origem de um redirecionamento pode ser um nome de arquivo ou o nome de um fluxo (como & 1, pelo menos no bash).Mas os pipes separam totalmente um comando de outro, você não pode misturá-los com argumentos:
O canal pega tudo o que foi gravado na saída padrão do comando1 e o envia para a entrada padrão do comando2.
Você também pode combinar tubulação e redirecionamento. Por exemplo:
A primeira
cat
lerá as linhas de infile, depois escreverá cada linha para outfile e a enviará para a segundacat
.Na segunda
cat
, a entrada padrão lê primeiro a partir do canal (o conteúdo de infile), depois lê a partir de infile2, escrevendo cada linha em outfile2. Depois de executar isso, o arquivo de arquivamento será uma cópia de infile e o arquivo de arquivamento2 conterá o infile seguido por infile2.Finalmente, você realmente faz algo realmente semelhante ao seu exemplo usando o redirecionamento "here string" (somente família bash) e backticks:
dará o mesmo resultado que
Mas acho que a versão de redirecionamento primeiro lê toda a saída de ls em um buffer (na memória) e depois alimenta esse buffer para grep uma linha de cada vez, enquanto a versão canalizada pega cada linha de ls à medida que surge, e passe essa linha para grep.
fonte
echo yes 1>&2 2>/tmp/blah; wc -l /tmp/blah; echo yes 2>/tmp/blah 1>&2; wc -l /tmp/blah
Além disso, o redirecionamento para um arquivo usará apenas o último redirecionamento.echo yes >/tmp/blah >/tmp/blah2
só vai escrever para/tmp/blah2
.Nota: A resposta reflete meu próprio entendimento desses mecanismos atualizados, acumulados durante a pesquisa e leitura das respostas pelos colegas deste site e do unix.stackexchange.com , e serão atualizados com o passar do tempo. Não hesite em fazer perguntas ou sugerir melhorias nos comentários. Eu também sugiro que você tente ver como os syscalls funcionam no shell com o
strace
comando Além disso, não se deixe intimidar pela noção de chamadas internas ou de syscalls - você não precisa saber ou ser capaz de usá-las para entender como o shell faz as coisas, mas elas definitivamente ajudam a entender.TL; DR
|
pipes não estão associados a uma entrada no disco, portanto, não possuem um número de inode do sistema de arquivos em disco (mas possuem inode no sistema de arquivos virtual pipefs no espaço do kernel), mas os redirecionamentos geralmente envolvem arquivos que possuem entradas de disco e, portanto, têm o correspondente inode.lseek()
'capazes', de modo que os comandos não podem ler alguns dados e depois retroceder, mas quando você redireciona>
ou<
geralmente é um arquivo que é umlseek()
objeto capaz, os comandos podem navegar da maneira que desejarem.dup2()
syscalls embaixo do capô para fornecer cópias dos descritores de arquivo, onde ocorre o fluxo real de dados.exec
comando interno (veja isso e isso ); portanto, se você fizer isso,exec > output.txt
todos os comandos gravarão aoutput.txt
partir de então.|
os pipes são aplicados apenas ao comando atual (o que significa comando simples ou subconjuntos, comoseq 5 | (head -n1; head -n2)
comandos compostos.Quando o redirecionamento é feito nos arquivos, coisas como
echo "TEST" > file
eecho "TEST" >> file
usam oopen()
syscall nesse arquivo ( veja também ) e obtêm o descritor de arquivos para o qual ele é passadodup2()
. Os tubos|
usam apenaspipe()
edup2()
syscall.No que diz respeito à execução de comandos, os pipes e o redirecionamento não passam de descritores de arquivos - objetos semelhantes a arquivos, nos quais eles podem escrever às cegas, ou manipulá-los internamente (o que pode produzir comportamentos inesperados;
apt
por exemplo, tende a nem mesmo gravar no stdout se souber que há redirecionamento).Introdução
Para entender como esses dois mecanismos diferem, é necessário entender suas propriedades essenciais, a história por trás dos dois e suas raízes na linguagem de programação C. De fato, é essencial saber o que são os descritores de arquivos e como as chamadas de sistema
dup2()
e depipe()
trabalholseek()
. O shell é uma maneira de tornar esses mecanismos abstratos para o usuário, mas aprofundar mais do que a abstração ajuda a entender a verdadeira natureza do comportamento do shell.As origens dos redirecionamentos e tubulações
De acordo com o artigo de Dennis Ritche, Petroglyphs proféticos , os canos se originaram de um memorando interno de 1964 de Malcolm Douglas McIlroy , na época em que estavam trabalhando no sistema operacional Multics . Citar:
O que é aparente é que, na época, os programas eram capazes de gravar no disco, mas isso era ineficiente se a saída fosse grande. Para citar a explicação de Brian Kernighan no vídeo do Unix Pipeline :
Assim, a diferença conceitual é aparente: os tubos são um mecanismo para fazer os programas conversarem entre si. Redirecionamentos - são uma maneira de escrever no arquivo no nível básico. Nos dois casos, o shell facilita essas duas coisas, mas por baixo do capô há muita coisa acontecendo.
Indo mais fundo: syscalls e funcionamento interno do shell
Começamos com a noção de descritor de arquivo . Os descritores de arquivo descrevem basicamente um arquivo aberto (seja um arquivo no disco, na memória ou no arquivo anônimo), representado por um número inteiro. Os dois fluxos de dados padrão (stdin, stdout, stderr) são descritores de arquivo 0,1 e 2, respectivamente. De onde eles vêm ? Bem, nos comandos shell, os descritores de arquivo são herdados de seus pais - shell. E é verdade em geral para todos os processos - o processo filho herda os descritores de arquivo dos pais. Para daemons , é comum fechar todos os descritores de arquivos herdados e / ou redirecionar para outros locais.
Voltar para o redirecionamento. O que é realmente? É um mecanismo que instrui o shell a preparar descritores de arquivo para o comando (porque os redirecionamentos são feitos pelo shell antes da execução do comando) e aponte-os para onde o usuário sugeriu. A definição padrão de redirecionamento de saída é
Que
[n]
existe o número do descritor de arquivo. Quando você fazecho "Something" > /dev/null
o número 1 está implícito lá, eecho 2> /dev/null
.Sob o capô, isso é feito duplicando o descritor de arquivo por meio de uma
dup2()
chamada do sistema. Vamos levardf > /dev/null
. O shell criará um processo filho onde serádf
executado, mas antes disso será aberto/dev/null
como o descritor de arquivo nº 3 edup2(3,1)
será emitido, o que fará uma cópia do descritor de arquivo 3 e a cópia será 1. Você sabe como possui dois arquivosfile1.txt
efile2.txt
, e quando vocêcp file1.txt file2.txt
tiver dois arquivos iguais, mas poderá manipulá-los independentemente? É o mesmo que acontece aqui. Freqüentemente, você pode ver que, antes de executar,bash
serádup(1,10)
necessário criar um descritor de arquivo de cópia nº 1, que éstdout
(e essa cópia será o arquivo nº 10), a fim de restaurá-lo posteriormente. Importante é observar que, quando você considera comandos internos(que fazem parte do próprio shell e não têm arquivo em/bin
outro local) ou comandos simples no shell não interativo , o shell não cria um processo filho.E então temos coisas como
[n]>&[m]
e[n]&<[m]
. Isso está duplicando os descritores de arquivo, que possuem o mesmo mecanismo quedup2()
apenas agora está na sintaxe do shell, convenientemente disponível para o usuário.Uma das coisas importantes a serem observadas sobre o redirecionamento é que seu pedido não é fixo, mas é significativo na maneira como o shell interpreta o que o usuário deseja. Compare o seguinte:
O uso prático destes em scripts de shell pode ser versátil:
e muitos outros.
Encanamento com
pipe()
edup2()
Então, como os tubos são criados? Via
pipe()
syscall , que terá como entrada uma matriz (também conhecida como lista) chamadapipefd
de dois itens do tipoint
(inteiro). Esses dois números inteiros são descritores de arquivo. Opipefd[0]
final da leitura do pipe epipefd[1]
o final da gravação. Assimdf | grep 'foo'
,grep
obterá uma cópiapipefd[0]
edf
receberá uma cópia depipefd[1]
. Mas como ? Claro, com a magia dodup2()
syscall. Nodf
nosso exemplo, digamos quepipefd[1]
tenha o número 4, então o shell criará um filho, façadup2(4,1)
(lembra do meucp
exemplo?) E depois executeexecve()
para realmente executardf
. Naturalmente,df
herdará o descritor de arquivo nº 1, mas não saberá que não está mais apontando para o terminal, mas sim o nº 4, que é realmente o final de gravação do canal. Naturalmente, a mesma coisa ocorrerá,grep 'foo'
exceto com diferentes números de descritores de arquivos.Agora, pergunta interessante: poderíamos fazer pipes que redirecionam também o FD # 2, e não apenas o FD # 1? Sim, na verdade é isso que
|&
acontece no bash. O padrão POSIX requer linguagem de comandos shell para suportardf 2>&1 | grep 'foo'
sintaxe para esse fim, masbash
faz|&
bem.O que é importante observar é que os pipes sempre lidam com descritores de arquivos. Existe
FIFO
ou pipe nomeado , que tem um nome de arquivo no disco e de você usá-lo como um arquivo de deixar, mas se comporta como um tubo. Mas os|
tipos de pipes são conhecidos como pipe anônimo - eles não têm nome de arquivo, porque na verdade são apenas dois objetos conectados. O fato de não estarmos lidando com arquivos também cria uma implicação importante: os pipes não sãolseek()
capazes. Os arquivos, na memória ou no disco, são estáticos - os programas podem usar olseek()
syscall para pular para o byte 120, depois voltar para o byte 10 e depois avançar até o final. Os pipes não são estáticos - eles são seqüenciais e, portanto, você não pode retroceder os dados que obtém deles comlseek()
. É isso que faz com que alguns programas saibam se estão lendo do arquivo ou do pipe e, portanto, podem fazer os ajustes necessários para obter um desempenho eficiente; em outras palavras, umprog
pode detectar se eu façocat file.txt | prog
ouprog < input.txt
. Exemplo de trabalho real disso é cauda .As outras duas propriedades muito interessantes dos pipes são que eles têm um buffer, que no Linux tem 4096 bytes , e eles realmente têm um sistema de arquivos conforme definido no código-fonte do Linux ! Eles não são simplesmente um objeto para a transmissão de dados, são eles mesmos uma estrutura de dados! De fato, como existe o sistema de arquivos pipefs, que gerencia os pipes e os FIFOs, os pipes têm um número de inode em seu respectivo sistema de arquivos:
No Linux, os pipes são unidirecionais, assim como o redirecionamento. Em algumas implementações do tipo Unix - existem tubos bidirecionais. Embora com a mágica do script de shell, você também possa criar tubos bidirecionais no Linux .
Veja também:
pipe()
syscall edup2()
.<<
,<<<
são implementados como arquivos temporários anônimos (desvinculados)bash
eksh
, enquanto< <()
usam pipes anônimos;/bin/dash
usa tubos para<<
. Consulte Qual é a diferença entre <<, <<< e <<no bash?fonte
Para adicionar às outras respostas, também existem diferenças semânticas sutis - por exemplo, os pipes fecham mais facilmente do que os redirecionamentos:
No primeiro exemplo, quando a primeira chamada
head
termina, ela fecha o canal eseq
termina, para que não haja entrada disponível para a segundahead
.No segundo exemplo, o cabeçote consome a primeira linha, mas quando fecha seu próprio
stdin
canal , o arquivo permanece aberto para a próxima chamada a ser usada.O terceiro exemplo mostra que, se usarmos
read
para evitar o fechamento do tubo, ele ainda estará disponível no subprocesso.Portanto, o "fluxo" é o que desviaremos os dados (stdin etc.) e é o mesmo nos dois casos, mas o canal conecta fluxos de dois processos, em que um redirecionamento conecta fluxos entre um processo e um arquivo, para que você pode ver a fonte das semelhanças e diferenças.
PS Se você está tão curioso e / ou surpreso com esses exemplos como eu, pode se aprofundar ainda mais usando
trap
para ver como os processos são resolvidos, por exemplo:Às vezes, o primeiro processo fecha antes da
1
impressão, às vezes depois.Também achei interessante usar
exec <&-
para fechar o fluxo do redirecionamento para aproximar o comportamento do pipe (embora com um erro):fonte
read
consome apenas a primeira linha (que é um byte1
e nova linha).seq
enviado no total 10 bytes (5 números e 5 novas linhas). Portanto, restam 8 bytes no buffer do pipe, e é por isso que o segundohead
funciona - ainda há dados disponíveis no buffer do pipe. Btw, cabeça sai somente se houver 0 bytes ler, meio como emhead /dev/null
seq 5 | (head -n1; head -n1)
primeira chamada esvazia o canal, então ele ainda existe em um estado aberto, mas sem dados para a segunda chamadahead
? Portanto, a diferença de comportamento entre o canal e o redirecionamento é porque o head retira todos os dados do canal, mas apenas as duas linhas do identificador de arquivo?strace
comando que dei no primeiro comentário. Com o redirecionamento, o arquivo tmp está no disco, o que o torna procurável (porque eles usam olseek()
syscall - os comandos podem pular o arquivo do primeiro byte para o último da maneira que quiserem. Mas os pipes são seqüenciais e não podem ser procurados. trabalho é ler tudo primeiro, ou se o arquivo é grande - mapa alguns deles para a RAM através demmap()
chamada uma vez que eu fiz a minha própria.tail
em Python, e correu exatamente mesmo problema.(...)
, e o subshell fará uma cópia de seu próprio stdin para cada comando interno(...)
. Portanto, eles são tecnicamente lidos do mesmo objeto. Primeiro,head
pensa que está lendo seu próprio estilo. Segundohead
pensa que tem seu próprio estilo. Mas, na realidade, o fd # 1 (stdin) é apenas uma cópia do mesmo fd, que é o fim do tubo. Além disso, eu postei uma resposta, então talvez isso ajude a esclarecer as coisas.Eu encontrei um problema com isso em C hoje. Essencialmente, os Pipe também têm semânticas diferentes para redirecionar, mesmo quando enviadas para
stdin
. Realmente acho que, dadas as diferenças, os pipes devem ir para outro lugar que nãostdin
, para que possamosstdin
chamá-lostdpipe
(para fazer um diferencial arbitrário) ser tratado de maneiras diferentes.Considere isto. Ao canalizar uma saída de programa para outra,
fstat
parece retornar zero,st_size
apesar dels -lha /proc/{PID}/fd
mostrar que há um arquivo. Ao redirecionar um arquivo este não é o caso (pelo menos no debianwheezy
,stretch
ejessie
baunilha e ubuntu14.04
,16.04
baunilha.Se você
cat /proc/{PID}/fd/0
redirecionar, poderá repetir a leitura quantas vezes quiser. Se você fizer isso com um pipe, notará que, na segunda vez em que executa a tarefa consecutivamente, não obtém a mesma saída.fonte