matklad / xshell

Apache License 2.0
675 stars 28 forks source link

Returning Child with spawn #76

Open npajkovsky opened 11 months ago

npajkovsky commented 11 months ago

Hey,

I'm enjoying your crate, but I hit the wall. Let's say, I would like to execute a command that outputs in threads

 all_servers.par_iter().try_for_each(op: |server: &Server| {
    cmd!(
        sh,
        "command with output"
    )

what happens that the output interleaves, and I have no way to know which thread outputs what. The only solution is to read the output and prefix it with Id, but that is not possible with your crate, right?

matklad commented 11 months ago

Yeah, there's nothing built-in for this yet. What should be happening on the level of syscalls here is that each cmd! process should get its stdout/stderr redirected to a pipe (so, two pipes per process). Than, in the host process something should do an epoll loop over these pipes, reading the output and notifiying the user's code that "hey, process #k emitted output foo".

It's not at all clear how to express this functionality in xshell APIs (though it should be possible). A related thing here is that xshell already spawns thread behind user's back, so it won't be inconceivable to do more of that:

https://github.com/matklad/xshell/blob/ebe98b4d4845f23ffdeeb4c69e097bd72a0f1e95/src/lib.rs#L1036

I think the best API here, if we don't go full tokio/async would probably be something along the lines of

impl Cmd {
    fn stream_stderr(self, callback: impl FnMut(&mut [u8]) -> usize);
    fn stream_stderr_lines(self, callback: impl FnMut(&str));
}

Definitely in scope for the crate, but I am unlikely to work on it myself