Qual é a diferença entre "Redirection" e "Pipe"?

205

Essa pergunta pode parecer um pouco estúpida, mas não consigo ver a diferença entre redirecionamento e pipes.

O redirecionamento é usado para redirecionar o stdout / stdin / stderr, por exemplo ls > log.txt.

Tubos são usados ​​para fornecer a saída de um comando como entrada para outro comando, por exemplo ls | grep file.txt.

Mas por que existem dois operadores para a mesma coisa?

Por que não escrever apenas ls > greppara passar a saída, isso também não é apenas um tipo de redirecionamento? O que estou perdendo?

John Threepwood
fonte

Respostas:

224

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 > thing2vsthing1 | thing2

thing1 > thing2

  1. Seu shell executará o programa chamado thing1
  2. Tudo o que thing1sai será colocado em um arquivo chamado thing2. (Nota - se thing2existir, será substituído)

Se você deseja passar a saída do programa thing1para um programa chamado thing2, você pode fazer o seguinte:

thing1 > temp_file && thing2 < temp_file

qual seria

  1. executar o programa chamado thing1
  2. salve a saída em um arquivo chamado temp_file
  3. execute o programa nomeado thing2, fingindo que a pessoa no teclado digitou o conteúdo temp_filecomo entrada.

No entanto, isso é desajeitado, então eles fizeram tubos como uma maneira mais simples de fazer isso. thing1 | thing2faz 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.txtele 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).

David Oneill
fonte
Obrigado. Você mencionou thing1 > temp_file && thing2 < temp_fileque é mais fácil usar canos. Mas por que não reutilizar o >operador para fazer isso, por exemplo, thing1 > thing2para comandos thing1e thing2? Por que um operador extra |?
precisa
1
"Pegue a saída e grave-a em um arquivo" é uma ação diferente de "Pegue a saída e passe-a para um programa diferente". Vou editar mais pensamentos em minha resposta ...
David Oneill
1
@JohnThreepwood Eles têm significados diferentes. E se eu quisesse redirecionar algo para um arquivo chamado less, por exemplo? thing | lesse thing > lesssão perfeitamente diferentes, pois fazem coisas diferentes. O que você propõe criaria uma ambiguidade.
Darkhogg
É correto dizer que "thing1> temp_file" é apenas um açúcar sintático para "thing1 | tee temp_file"? Desde que descobri o tee, quase nunca uso redirecionamentos.
Sridhar Sarnobat
2
@ Sridhar-Sarnobat não, o teecomando faz algo diferente. teegrava a saída na tela ( stdout) e no arquivo. Redirecionar faz apenas o arquivo.
precisa
22

Se o significado de foo > bardepender de se existe um comando nomeado barque 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.

Florian Diesch
fonte
Isso seria um problema apenas se você estiver gravando barem um diretório que faça parte da sua $PATHvariável env. Se você estiver em algo como / bin, ot pode ser um problema. Mas, mesmo assim, barteria que ter uma permissão de executável definida, para que o shell verifique não apenas a localização de um executável, barmas também possa executá-lo. E se a preocupação for sobrescrever arquivo existente, a nocloberopção shell deve impedir a substituição de arquivos existentes nos redirecionamentos.
Sergiy Kolodyazhnyy 12/09
13

No Manual de Administração de Sistemas Unix e Linux:

Redirecionamento

O shell interpreta os símbolos <,> e >> como instruções para redirecionar a entrada ou saída de um comando para ou de um arquivo .

Tubos

Para conectar o STDOUT de um comando ao STDIN de outro, use o | símbolo, vulgarmente conhecido como tubo.

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.

Mr Whatever
fonte
12

Há uma diferença vital entre os dois operadores:

  1. ls > log.txt -> Este comando envia a saída para o arquivo log.txt.

  2. 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:

ls > log.txt; grep 'file.txt' log.txt

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.

Ankit
fonte
3

Há uma grande diferença sintática entre os dois:

  1. Um redirecionamento é um argumento para um programa
  2. Um tubo separa dois comandos

Você pode pensar de redirecionamentos como este: cat [<infile] [>outfile]. Isso implica que a ordem não importa: cat <infile >outfileé o mesmo que cat >outfile <infile. Você pode até misturar redirecionamentos com outros argumentos: cat >outfile <infile -be cat <infile -b >outfileambos 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:

[command1] | [command2]

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:

cat <infile >outfile | cat <infile2 >outfile2

A primeira catlerá as linhas de infile, depois escreverá cada linha para outfile e a enviará para a segunda cat.

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:

grep blah <<<`ls`

dará o mesmo resultado que

ls | grep blah

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.

user319857
fonte
1
Nitpick: ordem é importante no redirecionamento se você redirecionar um fd para outro: echo yes 1>&2 2>/tmp/blah; wc -l /tmp/blah; echo yes 2>/tmp/blah 1>&2; wc -l /tmp/blahAlém disso, o redirecionamento para um arquivo usará apenas o último redirecionamento. echo yes >/tmp/blah >/tmp/blah2só vai escrever para /tmp/blah2.
Muru
2
Redirecionar não é realmente um argumento para o programa. O programa não saberá nem se importará para onde vai sua saída (ou a entrada vem). É apenas uma maneira de dizer ao bash como organizar as coisas antes de executar o programa.
Alois Mahdal
3

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 stracecomando 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.
  • os pipes não são 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 é um lseek()objeto capaz, os comandos podem navegar da maneira que desejarem.
  • redirecionamentos são manipulações nos descritores de arquivos, que podem ser muitos; pipes possuem apenas dois descritores de arquivo - um para o comando esquerdo e outro para o comando direito
  • o redirecionamento em fluxos e tubulações padrão são armazenados em buffer.
  • os tubos quase sempre envolvem bifurcação e, portanto, pares de processos estão envolvidos; redirecionamentos - nem sempre, embora nos dois casos os descritores de arquivo resultantes sejam herdados por subprocessos.
  • os pipes sempre conectam descritores de arquivo (um par), redirecionamentos - use um nome de caminho ou descritores de arquivo.
  • pipes são o método de comunicação entre processos, enquanto os redirecionamentos são apenas manipulações em arquivos abertos ou objetos semelhantes a arquivos
  • ambos empregam dup2()syscalls embaixo do capô para fornecer cópias dos descritores de arquivo, onde ocorre o fluxo real de dados.
  • os redirecionamentos podem ser aplicados "globalmente" com o execcomando interno (veja isso e isso ); portanto, se você fizer isso, exec > output.txttodos os comandos gravarão a output.txtpartir de então. |os pipes são aplicados apenas ao comando atual (o que significa comando simples ou subconjuntos, como seq 5 | (head -n1; head -n2)comandos compostos.
  • Quando o redirecionamento é feito nos arquivos, coisas como echo "TEST" > filee echo "TEST" >> fileusam o open()syscall nesse arquivo ( veja também ) e obtêm o descritor de arquivos para o qual ele é passado dup2(). Os tubos |usam apenas pipe()e dup2()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; aptpor 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 de pipe()trabalho lseek(). 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:

Para resumir minhas preocupações mais fortes:

  1. Deveríamos ter algumas maneiras de conectar programas, como mangueira de jardim - enroscar em outro segmento quando se tornar necessário massagear dados de outra maneira. Este é o caminho do IO também.

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 :

Primeiro, você não precisa escrever um grande programa maciço - você tem programas menores existentes que já podem fazer parte do trabalho ... Outro é que é possível que a quantidade de dados que você está processando não se encaixe você armazenou em um arquivo ... porque lembre-se, estamos de volta aos dias em que esses discos tinham, se você tivesse sorte, um megabyte ou dois dados ... Portanto, o pipeline nunca teve que instanciar toda a saída .

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 é

[n]>word

Que [n]existe o número do descritor de arquivo. Quando você faz echo "Something" > /dev/nullo número 1 está implícito lá, e echo 2> /dev/null.

Sob o capô, isso é feito duplicando o descritor de arquivo por meio de uma dup2()chamada do sistema. Vamos levar df > /dev/null. O shell criará um processo filho onde será dfexecutado, mas antes disso será aberto /dev/nullcomo o descritor de arquivo nº 3 e dup2(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 arquivos file1.txte file2.txt, e quando você cp file1.txt file2.txttiver dois arquivos iguais, mas poderá manipulá-los independentemente? É o mesmo que acontece aqui. Freqüentemente, você pode ver que, antes de executar, bashserá 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 /binoutro 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 que dup2()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:

# Make copy of where fd 2 points , then redirect fd 2
$ ls -l /proc/self/fd/  3>&2  2> /dev/null
total 0
lrwx------ 1 user user 64 Sep 13 00:08 0 -> /dev/pts/0
lrwx------ 1 user user 64 Sep 13 00:08 1 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:08 2 -> /dev/null
lrwx------ 1 runner user 64 Sep 13 00:08 3 -> /dev/pts/0
lr-x------ 1 user user 64 Sep 13 00:08 4 -> /proc/29/fd

# redirect fd #2 first, then clone it
$ ls -l /proc/self/fd/    2> /dev/null 3>&2
total 0
lrwx------ 1 user user 64 Sep 13 00:08 0 -> /dev/pts/0
lrwx------ 1 user user 64 Sep 13 00:08 1 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:08 2 -> /dev/null
l-wx------ 1 user user 64 Sep 13 00:08 3 -> /dev/null
lr-x------ 1 user user 64 Sep 13 00:08 4 -> /proc/31/fd

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) chamada pipefdde dois itens do tipo int(inteiro). Esses dois números inteiros são descritores de arquivo. O pipefd[0]final da leitura do pipe e pipefd[1]o final da gravação. Assim df | grep 'foo', grepobterá uma cópia pipefd[0]e dfreceberá uma cópia de pipefd[1]. Mas como ? Claro, com a magia do dup2()syscall. No dfnosso exemplo, digamos que pipefd[1]tenha o número 4, então o shell criará um filho, faça dup2(4,1)(lembra do meu cpexemplo?) E depois execute execve()para realmente executar df. Naturalmente,dfherdará 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 suportar df 2>&1 | grep 'foo'sintaxe para esse fim, mas bashfaz |&bem.

O que é importante observar é que os pipes sempre lidam com descritores de arquivos. Existe FIFOou 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ão lseek()capazes. Os arquivos, na memória ou no disco, são estáticos - os programas podem usar o lseek()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, um progpode detectar se eu faço cat file.txt | progou prog < 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:

# Stdout of ls is wired to pipe
$ ls -l /proc/self/fd/  | cat  
lrwx------ 1 user user 64 Sep 13 00:02 0 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:02 1 -> pipe:[15655630]
lrwx------ 1 user user 64 Sep 13 00:02 2 -> /dev/pts/0
lr-x------ 1 user user 64 Sep 13 00:02 3 -> /proc/22/fd
# stdin of ls is wired to pipe
$ true | ls -l /proc/self/fd/0
lr-x------ 1 user user 64 Sep 13 03:58 /proc/self/fd/0 -> 'pipe:[54741]'

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:

Sergiy Kolodyazhnyy
fonte
2

Para adicionar às outras respostas, também existem diferenças semânticas sutis - por exemplo, os pipes fecham mais facilmente do que os redirecionamentos:

seq 5 | (head -n1; head -n1)                # just 1
seq 5 > tmp5; (head -n1; head -n1) < tmp5   # 1 and 2
seq 5 | (read LINE; echo $LINE; head -n1)   # 1 and 2

No primeiro exemplo, quando a primeira chamada headtermina, ela fecha o canal e seqtermina, para que não haja entrada disponível para a segunda head.

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 readpara 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 trappara ver como os processos são resolvidos, por exemplo:

(trap 'echo seq EXITed >&2' EXIT; seq 5) | (trap 'echo all done' EXIT; (trap 'echo first head exited' EXIT; head -n1)
echo '.'
(trap 'echo second head exited' EXIT; head -n1))

Às vezes, o primeiro processo fecha antes da 1impressã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):

seq 5 > tmp5
(trap 'echo all done' EXIT
(trap 'echo first head exited' EXIT; head -n1)
echo '.'
exec <&-
(trap 'echo second head exited' EXIT; head -n1)) < tmp5`
Julian de Bhal
fonte
"quando a primeira chamada à cabeça termina, ela fecha o tubo" Isso é realmente impreciso por dois motivos. Um, (head -n1; head -n1) é um subshell com dois comandos, cada um dos quais herda o fim do pipe de leitura como descritor 0 e, portanto, o subshell AND cada comando tem esse descritor de arquivo aberto. Segunda razão, você pode ver que com strace -f bash -c 'seq 5 | (cabeça -n1; cabeça -n1) '. Então, primeiro cabeça fecha apenas a sua cópia do descritor de arquivo
Sergiy Kolodyazhnyy
O terceiro exemplo também é impreciso, porque readconsome apenas a primeira linha (que é um byte 1e nova linha). seqenviado no total 10 bytes (5 números e 5 novas linhas). Portanto, restam 8 bytes no buffer do pipe, e é por isso que o segundo headfunciona - ainda há dados disponíveis no buffer do pipe. Btw, cabeça sai somente se houver 0 bytes ler, meio como emhead /dev/null
Sergiy Kolodyazhnyy
Obrigado pelo esclarecimento. Estou entendendo corretamente que na 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 chamada head? 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?
Julian de Bhal 12/09
Está correto. E é algo que pode ser visto com o stracecomando que dei no primeiro comentário. Com o redirecionamento, o arquivo tmp está no disco, o que o torna procurável (porque eles usam o lseek()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 de mmap()chamada uma vez que eu fiz a minha própria. tailem Python, e correu exatamente mesmo problema.
Sergiy Kolodyazhnyy
Também é importante lembrar que o fim da leitura do pipe (descritor de arquivo) é fornecido para o subshell primeiro (...), 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. Segundo headpensa 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.
Sergiy Kolodyazhnyy 12/09
1

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ão stdin, para que possamos stdinchamá-lo stdpipe(para fazer um diferencial arbitrário) ser tratado de maneiras diferentes.

Considere isto. Ao canalizar uma saída de programa para outra, fstatparece retornar zero, st_sizeapesar de ls -lha /proc/{PID}/fdmostrar que há um arquivo. Ao redirecionar um arquivo este não é o caso (pelo menos no debian wheezy, stretche jessiebaunilha e ubuntu 14.04, 16.04baunilha.

Se você cat /proc/{PID}/fd/0redirecionar, 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.

MrMesees
fonte