Input/output to an external process

Hi,

I want to both read and write to an external process.
It seems that if e.g. I just want to read or write to a process, there’s a nice way of doing that (from the docs):

open(`less`, "w", stdout) do io
     for i = 1:3
         println(io, i)
     end
 end

However, if I want to do both, the best I can find is resorting to a hack (from Gaston.jl):

pin = Pipe()
pout = Pipe()
perr = Pipe()
proc = run(pipeline(`cat`, stdin = pin, stdout = pout, stderr = perr), wait = false)
process_running(proc) || error("not running")
close(pout.in)
close(perr.in)
close(pin.out)

write(pin, "test\n")
@show(readline(pout))

# output:
# readline(pout) = "test"

Surely there’s a better way…

Thanks,

DD

Have a look at
https://github.com/emmt/InterProcessCommunication.jl
I don’t know whats the state of this package but you will get some hints out of the package to what you can look for. I found it searching for “semaphore” for Julia. Semaphores are as I remember some typical way for inter process communications.

Thanks, but this seems like an implementation of various IPC primitives such as semaphores, locks, etc.
It doesn’t seem to deal with starting a process…

Using the code above, I can hack something that hides the ugly in minutes:

function wrap(f, cmd::Cmd)
    pin = Pipe()
    pout = Pipe()
    perr = Pipe()
    proc = run(pipeline(cmd, stdin = pin, stdout = pout, stderr = perr), wait = false)
    process_running(proc) || error("not running")
    close(pout.in)
    close(perr.in)
    close(pin.out)
    
    return f(pin, pout, perr)
end


wrap(`cat`) do pin, pout, perr
     println(pin, "test")
     println(readline(pout))
end

# outputs: test

I’m surprised there’s nothing similar in the standard library (which will handle all the edge cases my hack above doesn’t).

DD

Probably because that specific use case is a little niche, especially without returning the Process object itself (e.g. to chain it via another pipeline or introspect exit status…). There’s also a possible ambiguity in regards to whether f or wrap should close the created Pipes, as some functions may want to write to that stdin of the spawned process for some initial setup (which you can’t do if wrap already closed them). Additionally, it’s debatable whether f or wrap should close the pipe inputs/outputs at all, as usually the do notation in functions with sideeffects is used to clean up/do ressource management (and thus could close the pipe ends…). Exposing those options as part of wrap would not be much less “overhead” than writing the Pipes and close calls yourself, for your specific usecase.

1 Like