Read stdin and stdout from processes simultaneously


#1

I’m starting a new process using Command::new and am trying to read the stdin of the main process and the stdout of the child process simultaneously. One of then works perfectly fine in a while-loop, but as soon as I read both using read_to_end or even just read, it stops right there until there are more bytes available. But when there is no user input, it just stops at that read-command. How would you be able to read both of them without waiting at all?


#2

I probably won’t be able to help that much, but for future people looking at this it would probably be helpful to mention if you are using std::process::Command or the old std::old_io::process::Command.


#3

Hi, thanks for the tipp. I’m currently still using old_io, but would be willing to move to std::process to get it working.


#4

I think what you are looking for is non-blocking I/O. Forgive me if I’m explaining something you already know, but I’m going to ramble for a bit.

Blocking means that if you ask for a certain amount of data from {a file, stdin, a network socket etc}, and there isn’t that data there, it will wait until it is. This is useful, for example, if you want to read from stdin line by line, process each line as it comes, and then keep going, like a shell would do.

Non-blocking means that if you ask for, say, a line from the file, it will immediately return either a full line (if a full line is there), or immediately return a partial line or nothing if it gets to the end of what is currently in the stream.

That said, I don’t see a way to get stdin or Command::stdout to be non-blocking. Someone important: Is there non-blocking I/O? What you could do instead is use threads and channels, and have one thread reading stdin a line at a time, one thread reading from the Command a line at a time, and then use a channel to send it back to the main thread. Its a bit silly to be dedicating an entire thread just to reading from stdin or from a child process, but it does work…

I got curious, so I made an example. It uses cargo, so if you just clone the entire repository, it should run just fine with cargo run --release. Is that the sort of thing you’re looking for? I used std::io and std::process instead of old_io, but the basic idea should be much the same either way.


#5

Isn’t this a use case for select? I haven’t looked at how to do it in rust, but the general idea is the list of file descriptors get passed to select, and it returns those that are ready to read. I see a select! macro, but not sure how to use it on filehandles.


#6

Thank’s for the explanation, your example is pretty much exactly what I was looking for :smiley:. But I kinda feel like this should be made simpler, shouldn’t it? Like, isn’t non-blocking IO something you need pretty often?


#7

Yes, and after some digging, it looks like this was set aside in the IO Reform RFC, to be addressed later.

Back in January, @aturon submitted a PR to address this, but after some feedback, it was closed in favor of editing. I can’t find anything newer than that, but I can’t imagine that its been forgotten.

Yes, I think it is, but I don’t think there is a select function in std that will work for this. There is std::select!, but that appears to only work for std::sync::mpsc::Receiver.