O canal é um arquivo aberto em um sistema de arquivos no kernel e não pode ser acessado como um arquivo regular em disco. Ele é armazenado em buffer automaticamente apenas em um determinado tamanho e, eventualmente, será bloqueado quando estiver cheio. Diferentemente dos arquivos originados em dispositivos de bloco, os pipes se comportam como dispositivos de caracteres e, portanto, geralmente não oferecem suporte, lseek()
e os dados lidos a partir deles não podem ser lidos novamente, como você faria com um arquivo comum.
A string here é um arquivo comum criado em um sistema de arquivos montado. O shell cria o arquivo e mantém seu descritor, removendo imediatamente seu único link do sistema de arquivos (e excluindo-o) antes de gravar / ler um byte no / 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 estiver lendo esse descritor tiver a capacidade de fazê-lo, poderá ser retrocedida lseek()
e lida novamente.
Nos dois casos, os tokens <<<
e |
representam descritores de arquivos e não necessariamente os próprios arquivos. Você pode ter uma idéia 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 here-string / doc é praticamente um caso de uma vez - o shell grava todos os dados nele antes de oferecer o descritor de leitura para a criança. Por outro lado, o shell abre o canal nos descritores apropriados e extrai os filhos para gerenciar os do canal - e, portanto, é escrito / lido simultaneamente nas duas extremidades.
Essas distinções, no entanto, são geralmente verdadeiras. Até onde eu sei (isso não é tão longe assim), isso é verdade para praticamente todos os shell que manipulam a <<<
mão curta da string <<
here para um redirecionamento aqui do documento com a única exceção de yash
. yash
, busybox
, dash
, E outras ash
variantes tendem a voltar aqui-documentos com tubos, porém, e assim naqueles conchas realmente há muito pouca diferença entre os dois depois de tudo.
Ok - duas exceções. Agora que estou pensando sobre isso, ksh93
na verdade não funciona |
, mas lida com todo o negócio com soquetes - embora faça um arquivo tmp excluído para a <<<*
maioria dos outros. Além disso, ele apenas coloca as seções separadas de um pipeline em um ambiente de subconjuntos, que é uma espécie de eufemismo POSIX, pelo menos que age como um subconjuntos , e nem mesmo os garfos.
O fato é que os resultados de referência do @ PSkocik (o que é muito útil) aqui podem variar bastante por vários motivos, e a maioria deles depende da implementação. Para a configuração do documento aqui, os maiores fatores serão o ${TMPDIR}
tipo de sistema de arquivos de destino e a configuração / disponibilidade atual do cache e ainda mais a quantidade de dados a serem gravados. Para o tubo, ele terá o tamanho do próprio processo de casca, porque são feitas cópias para os garfos necessários. Dessa maneira, bash
é terrível na configuração do pipeline (para incluir substituições de $(
comando )
) - porque é grande e muito lento, mas com ksh93
isso praticamente não faz diferença.
Aqui está outro pequeno trecho de shell para demonstrar como um shell divide subshells para 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 pipe_who()
chamada em pipeline relata e o relatório de uma execução no shell atual se deve ao comportamento especificado por um (
subshell )
de reivindicar o pid do shell pai $$
quando ele é expandido. Embora bash
subshells definitivamente sejam processos separados, o $$
parâmetro shell especial não é uma fonte confiável dessas informações. Ainda assim, o sh
shell filho do subshell não recusa relatar com precisão o seu $PPID
.