It is possible to do so. As examples, both Gnuplot frontends (Gnuplot.jl and my own Gaston.jl) work like this. But doing so is not straightforward. Here’s how I do it. I’ll assume gnuplot is the external executable I want to communicate with and, as a bonus, I’ll also read from its
First, start the process and connect its streams to Julia pipes:
"Returns a new gnuplot process."
inp = Base.PipeEndpoint()
out = Base.PipeEndpoint()
err = Base.PipeEndpoint()
process = run(config.exec, inp, out, err, wait=false)
Now we need a function to send a message to the process and read its response. This part is tricky because you want to be able to read the entire response, but reading when there is no output will result in blocking and stop your code. My approach is to ask gnuplot to print what I call “sigils” to its
stderr. Then, I read the content of the pipes until I detect the sigils. This guarantees that the code does not block, and also gives me confidence that I have read everything that gnuplot has to say.
"Send string `message` to `process` and handle its response."
function gp_send(process::Base.Process, message::String)
message *= "\n"
write(process, message) # send user input to gnuplot
# ask gnuplot to return sigils when it is done
write(process, """set print '-'
gpout = readuntil(process, "GastonDone\n", keep=true)
gperr = readuntil(process.err, "GastonDone\n", keep=true)
# handle errors
gpout == "" && @warn "gnuplot crashed."
gperr = gperr[1:end-11]
gperr != "" && @info "gnuplot returned a message in STDERR:" gperr
return gpout, gperr
Finally, I have a function to quit the process:
write(process, "exit gnuplot\n")
You will certainly have to adapt this code to your use case, but I hope this example points you in the right direction.