Você está observando os resultados do que não foi padronizado com o POSIX.
POSIX não padroniza como o interpretador executa um pipe.
Com a histórica Bourne Shell, o programa mais à direita em um pipeline não é nem mesmo uma criança da casca principal. A razão para fazer isso dessa maneira é porque essa implementação é lenta, mas precisa de poucos códigos - importante se você tiver apenas 64 kB de memória. Como nessa variante, o comando read
é executado em um subprocesso, a designação de uma variável shell em um subprocesso não é visível no shell principal.
Os shells modernos como ksh
ou bosh
(o recente Bourne Shell) criam pipes de uma maneira em que todos os processos em um pipe são filhos diretos do shell principal e se o programa mais à direita é um shell embutido, é ainda executado pelo shell principal.
Tudo isso é necessário para permitir que o programa read
modifique uma variável shell do shell principal. Portanto, somente nessa variante (que BTW é a variante mais rápida) permite que o shell principal veja os resultados da atribuição de variável.
Em seu segundo exemplo, todo o loop while
é executado no mesmo subprocesso e, portanto, permite imprimir a versão modificada da variável shell.
Existe uma solicitação para adicionar suporte ao shell POSIX para obter informações sobre se algum dos comandos do pipeline tem um código de saída diferente de zero. Para conseguir isso, um shell deve ser implementado de forma que todos os programas de um pipeline sejam filhos diretos do shell principal. Isso está perto do que é necessário para permitir
echo foo | read val; echo $val
para fazer o que é esperado, desde então, apenas o requisito para executar a leitura no shell principal está faltando.