Um fluxo de arquivo é definido como :
fully buffered if and only if it can be determined not to refer to an interactive device
Como você está redirecionando para a entrada padrão, o stdin não é interativo e, portanto, é armazenado em buffer.
getchar
é uma função de fluxo e fará com que o buffer seja preenchido de o fluxo, consumindo esses bytes e, em seguida, retornar um único byte para você. system
apenas executa fork-exec , então o subprocesso herda todos os seus descritores de arquivos abertos. é. Quando bash
tentar ler a partir de sua entrada padrão, descobrirá que ela já está no final do arquivo porque todo o conteúdo já foi lido pelo seu processo pai.
No seu caso, você quer consumir apenas esse byte antes de passar para o processo filho, então:
The
setvbuf()
function may be used after the stream pointed to by stream is associated with an open file but before any other operation [...] is performed on the stream.
Assim, adicionar uma chamada adequada antes do getchar()
:
#include <stdio.h>
#include <stdlib.h>
int main() {
setvbuf(stdin, NULL, _IONBF, 0 );
getchar();
system("/bin/bash -i");
return 0;
}
fará o que você quiser, configurando stdin
para não ser buffer ( _IONBF
). getchar
fará com que apenas um único byte seja lido e o restante da entrada estará disponível para o sub-processo. Talvez seja melhor usar read
em vez disso, evitando toda a interface de fluxos, nesse caso .
POSIX exige determinados comportamentos quando o identificador pode ser acessado de ambos os processos após uma bifurcação , mas observa explicitamente que
If the only action performed by one of the processes is one of the
exec
functions [...], the handle is never accessed in that process.
o que significa que system()
não (tem que) fazer nada de especial com ele, pois é apenas fork-exec .
Provavelmente, isso é o que sua solução fork
está atingindo. Se o identificador for acessado nos dois lados, , em seguida, o primeiro :
If the stream is open with a mode that allows reading and the underlying open file description refers to a device that is capable of seeking, the application shall either perform an
fflush()
, or the stream shall be closed.
Chamar fflush()
em um fluxo de leitura significa que:
the file offset of the underlying open file description shall be set to the file position of the stream
para que a posição do descritor seja redefinida de volta para 1 byte, o mesmo que o do fluxo, e o subprocesso subsequente receberá sua entrada padrão a partir desse ponto.
Além disso, para a segunda alça (filha) :
If any previous active handle has been used by a function that explicitly changed the file offset, except as required above for the first handle, the application shall perform an
lseek()
orfseek()
(as appropriate to the type of handle) to an appropriate location.
e suponho que "um local apropriado" possa ser o mesmo (embora não seja especificado). A chamada getchar()
"alterou explicitamente o deslocamento do arquivo", portanto, este caso deve ser aplicado. A intenção da passagem é que o trabalho em qualquer ramificação do fork tenha o mesmo efeito, portanto, fork() > 0
e fork() == 0
devem funcionar da mesma forma. Entretanto, como nada realmente acontece neste ramo, é discutível que nenhuma dessas regras deve ser usada para pais ou filhos.
O resultado exato é provavelmente dependente da plataforma - pelo menos, exatamente o que conta como "já pode ser acessado" não é especificado diretamente, nem qual identificador é primeiro e segundo. Há também um caso anterior e prioritário para o processo pai:
If the only further action to be performed on any handle to this open file descriptor is to close it, no action need be taken.
que indiscutivelmente se aplica para o seu programa, uma vez que apenas termina depois. Se isso acontecer, todos os casos restantes, incluindo o fflush()
, devem ser ignorados e o comportamento que você está vendo seria um desvio da especificação. É discutível que chamar fork()
constitua uma ação no identificador, mas não explícita ou óbvia, então eu não confiaria nisso. Há também o suficiente "ou" e "ou" nos requisitos que muita variação parece aceitável.
Por vários motivos , acho que o comportamento que você está vendo pode ser um bug , ou pelo menos uma interpretação generosa da especificação. Minha leitura geral é que, como em todos os casos, uma ramificação do fork
não faz nada, nenhuma dessas regras deveria ter sido aplicada e a posição do descritor deveria ter sido ignorada onde estava. Eu não posso ser definitivo sobre isso, mas parece a leitura mais direta.
Eu não confiaria na técnica fork
funcionando. Sua terceira versão não funciona para mim aqui. Use setbuf
/ setvbuf
. Se possível, eu até usaria popen
ou similar para configurar o processo com o filtragem necessária explicitamente, em vez de depender dos caprichos das interações do fluxo e do descritor de arquivo.