Sua análise até o momento está correta. A forma como um shell pode definir o stdin de um processo para um descritor de pipe pode ser (pseudocódigo):
pipe(p) // create a new pipe with two handles p[0] and p[1]
fork() // spawn a child process
close(p[0]) // close the write end of the pipe in the child
dup2(p[1], 0) // duplicate the pipe descriptor on top of fd 0 (stdin)
close(p[1]) // close the other pipe descriptor
exec() // run a new process with the new descriptors in place