Dados os 3 scripts a seguir:
printf 'a\nb\nc\n' > file && { head -n 1; cat; } < file
printf 'a\nb\nc\n' | { head -n 1; cat; }
{ head -n 1; cat; } < <(printf 'a\nb\nc\n')
Eu esperaria que a saída de cada um fosse:
a
b
c
mas para alguns deles, em alguns sistemas, esse não é o caso. Por exemplo, no cygwin:
$ printf 'a\nb\nc\n' > file && { head -n 1; cat; } < file
a
b
c
$ printf 'a\nb\nc\n' | { head -n 1; cat; }
a
$ { head -n 1; cat; } < <(printf 'a\nb\nc\n')
a
O que está causando a saída diferente desses scripts?
Informações adicionais - aparentemente, isso não é apenas um head
problema:
$ printf 'a\nb\nc\n' | { sed '1q'; cat; }
a
$ printf 'a\nb\nc\n' | { awk '1;{exit}'; cat; }
a
$ { sed '1q'; cat; } < <(printf 'a\nb\nc\n')
a
$ { awk '1;{exit}'; cat; } < <(printf 'a\nb\nc\n')
a
Qual seria uma maneira POSIX robusta no shell (ou seja, sem apenas invocar awk ou similar uma vez para fazer tudo) para ler um certo número de linhas da entrada e deixar o restante para um comando diferente, independentemente de a entrada vir de um canal ou um arquivo?
Esta pergunta foi inspirada por comentários em uma resposta para classificar todo o .csv com base no valor de uma determinada coluna .
head
pode ler toda a sua entrada. Ele deve ler pelo menos o que produz (caso contrário, é logicamente impossível de implementar), mas pode ler mais.Normalmente pede ao sistema operacional para ler um buffer
head
de tamanho fixo (chamando a chamada do sistema ou similar). Em seguida, ele procura caracteres de nova linha nesse buffer e imprime a saída até atingir o número desejado de linhas.read
Todas as implementações compatíveis com POSIX de
head
chamadalseek
para redefinir a posição do arquivo na entrada para ficar logo após o final da parte que foi copiada para a saída. No entanto, isso só é possível se o arquivo for pesquisável: isso inclui arquivos comuns, mas não pipes. Se a entrada for um pipe, tudo o quehead
foi lido é descartado do pipe e não pode ser colocado de volta. Isso explica a diferença que você observou entre<file
(arquivo regular) e|
ou<()
(pipe).A seção relevante da norma acima é:
Algumas
head
implementações, como ohead
built-in de ksh93 (ativado apósbuiltin head
, e desde que tenha sido incluído no tempo de compilação) também tentam não deixar o cursor além da última linha que ele tem como saída quando a entrada não é pesquisável, no caso de ksh93, lendo a entrada um byte de cada vez (como os shellsread
embutidos normalmente fazem) ou espiando o conteúdo dos canais antes de lê-lo no sistema que tem essa possibilidade (não no Linux). Mas esses são a exceção, pois há uma grande penalidade de desempenho.