O canal é um arquivo aberto em um sistema de arquivos no kernel e não pode ser acessado como um arquivo normal no disco. Ele é automaticamente armazenado em buffer apenas para um determinado tamanho e, eventualmente, bloqueará quando estiver cheio. Ao contrário dos arquivos originados em dispositivos de bloco, os pipes comportam-se muito como dispositivos de caracteres e, portanto, geralmente não suportam lseek()
e os dados lidos a partir deles não podem ser lidos novamente como você faria com um arquivo regular.
A string here é um arquivo regular criado em um sistema de arquivos montado. O shell cria o arquivo e retém seu descritor ao mesmo tempo em que remove imediatamente seu único link do sistema de arquivos (e, portanto, o exclui) antes de gravar / ler um byte para / do arquivo. O kernel manterá o espaço necessário para o arquivo até que todos os processos liberem todos os descritores para ele. Se a criança que lê a partir de tal descritor tiver a capacidade de fazê-lo, ela poderá ser rebobinada com lseek()
e lida novamente.
Em ambos os casos, os tokens <<<
e |
representam descritores de arquivos e não necessariamente os próprios arquivos. Você pode ter uma ideia melhor do que está acontecendo fazendo coisas como:
readlink /dev/fd/1 | cat
... ou ...
ls -l <<<'' /dev/fd/*
A diferença mais significativa entre os dois arquivos é que o aqui-string / doc é praticamente um caso todo-a-uma-vez - o shell grava todos os dados nele antes de oferecer o descritor lido até o filho. Por outro lado, o shell abre o pipe nos descritores apropriados e bifurca os filhos para gerenciar aqueles para o pipe - e por isso ele é gravado / lido simultaneamente nos dois extremos.
Essas distinções, no entanto, são apenas geralmente verdadeiras. Tanto quanto eu estou ciente (o que não é realmente tão longe) isso é verdade para praticamente todos os shell que lida com o short-hand <<<
here-string para <<
a here- redirecionamento de documentos com a única exceção de yash
. yash
, busybox
, dash
, e outras ash
variants tendem a voltar aqui - documentos com pipes, entretanto, e assim, naqueles shells há realmente pouca diferença entre os dois afinal.
Ok - duas exceções. Agora que estou pensando nisso, ksh93
não faz um pipe por |
, mas lida com todo o negócio w / sockets - embora ele faça um arquivo tmp excluído para <<<*
como a maioria outros fazem. Além disso, ele só coloca as seções separadas de um pipeline em um ambiente subshell que é um tipo de eufemismo POSIX para pelo menos ele age como um subshell , e assim não faz nem faça os garfos.
O fato é que os resultados do benchmark PSkocik (que é muito útil) aqui podem variar amplamente por muitos motivos, e a maioria deles depende da implementação. Para a configuração do documento aqui, os maiores fatores serão o ${TMPDIR}
do tipo de sistema de arquivos e a configuração / disponibilidade do cache atual, e ainda mais a quantidade de dados a serem gravados. Para o pipe, será o tamanho do próprio processo de shell, porque são feitas cópias dos garfos necessários. Desta forma, bash
é terrível na configuração do pipeline (para incluir $(
command )
substituições) - porque é grande e muito lento, mas com ksh93
dificilmente faz alguma diferença.
Aqui está outro pequeno snippet de shell para demonstrar como um shell separa os subshells de um pipeline:
pipe_who(){ echo "$$"; sh -c 'echo "$PPID"'; }
pipe_who
pipe_who | { pipe_who | cat /dev/fd/3 -; } 3<&0
32059 #bash's pid
32059 #sh's ppid
32059 #1st subshell's $$
32111 #1st subshell sh's ppid
32059 #2cd subshell's $$
32114 #2cd subshell sh's ppid
A diferença entre o que uma chamada de pipe_who()
encadeada reporta e o relatório de uma execução no shell atual é devido a um% p% de (
do comportamento especificado de reclamar o pid do shell pai em )
quando é expandido. Embora $$
subshells definitivamente sejam processos separados, o parâmetro de shell especial bash
não é uma fonte confiável dessas informações. Ainda assim, o filho da subshell $$
shell não declina de relatar com precisão seu sh
.