O problema aqui não é exatamente que head
ou awk
estão "fechando a entrada". Eles não têm escolha; qualquer programa fecha sua entrada quando termina, e isso é imposto pelo sistema operacional.
A questão é que a entrada padrão é um pipe, e os programas estão fazendo leituras em buffer. Não é possível não ler de um pipe, portanto, os dados que estiverem no readahead desaparecerão. Se, em vez de usar um pipe, você usar um arquivo, provavelmente verá que funciona bem:
#!/bin/bash
printf " %s \n" a b c d > /tmp/abcd
for ((i = 0 ; i < 2 ; i++)) ; do
echo "<< $i >>"
for ((j = 0 ; j < 2 ; j++)) ; do
read
echo "$REPLY"
done
done < /tmp/abcd
Pelo menos, isso funciona bem no Ubuntu. Você pode fazê-lo funcionar com um cano se você desativar o armazenamento em buffer - mas isso provavelmente tornará as coisas realmente lentas. Aqui está um pequeno programa em C que desativa o armazenamento em buffer e depois ecoa seu caractere de entrada por caractere até consumir o número solicitado de linhas:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv) {
int n = 1000;
if (argc > 1) n = atoi(argv[1]);
setvbuf(stdin, NULL, _IONBF, 0);
for (int ch = getchar(); ch != EOF; ch = getchar()) {
putchar(ch);
if (ch == '\n' && --n <= 0) break;
}
return n > 0;
}
Isso funcionou bem para mim (no Ubuntu, novamente - e você precisa compilá-lo com -std=c99
ou -std=c11
para que o compilador não reclame). É verdade que o programa não chama fclose(stdin)
, mas adicionar não fará qualquer diferença. Por outro lado, remover a chamada para setvbuf
provavelmente retornará ao sintoma que você observou com head
. (E também fará com que o programa execute um lote mais rápido.)
Se você tivesse o GNU split
em vez da versão BSD que acompanha o OS X, você poderia usar a sintaxe --filter=COMMAND
útil, que faz muito bem exatamente o que você quer; em vez de criar arquivos divididos, ele canaliza cada seção do arquivo para uma chamada do COMMAND especificado (e define a variável de ambiente $FILE
para o nome do arquivo esperado).