Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add STDERR pipe to readandwrite #11824

Closed
mbaz opened this issue Jun 23, 2015 · 5 comments · Fixed by #12739
Closed

Add STDERR pipe to readandwrite #11824

mbaz opened this issue Jun 23, 2015 · 5 comments · Fixed by #12739
Labels
domain:io Involving the I/O subsystem: libuv, read, write, etc.

Comments

@mbaz
Copy link
Contributor

mbaz commented Jun 23, 2015

Sometimes it is necessary to have access to a process' STDIN, STDOUT and STDERR. In my case, I need to open pipes to an external command (specifically, gnuplot) that outputs text to both STDOUT and STDERR. With current command readandwrite(), I can't read STDERR. This is on both 0.3 and master.

I can redirect the process' STDERR to a file with pipe(), but that is not good enough in my case. The reason is that the process is long-lived; I want to issue a command to it by writing to its STDIN, and then read both STDOUT and STDERR to see if the command was successful. This may be repeated any number of times.

I have tried redirecting STDERR to a PipeBuffer, but it doesn't work:

julia> S=PipeBuffer(1024)
IOBuffer(data=UInt8[...], readable=true, writable=true, seekable=false, append=true, size=0, maxsize=1024, ptr=1, mark=-1)
julia> readandwrite(pipe(`gnuplot`, stderr=S))
ERROR: MethodError: `redir_err` has no method matching redir_err(::Cmd, ::Base.AbstractIOBuffer{Array{UInt8,1}})
@tkelman tkelman added the domain:io Involving the I/O subsystem: libuv, read, write, etc. label Jun 23, 2015
@mbaz mbaz changed the title readandwrite should allow reading from STDERR Add STDERR pipe to readandwrite Jun 23, 2015
@mbaz
Copy link
Contributor Author

mbaz commented Jun 29, 2015

In the mean time, this function does what I need:

function popen3(cmd::Cmd)
    pin = Base.Pipe(C_NULL)
    cmd_pin = Base.Pipe(C_NULL)
    out = Base.Pipe(C_NULL)
    cmd_out = Base.Pipe(C_NULL)
    err = Base.Pipe(C_NULL)
    cmd_err = Base.Pipe(C_NULL)
    Base.link_pipe(pin, false, cmd_pin, true)
    Base.link_pipe(out, true, cmd_out, false)
    Base.link_pipe(err, true, cmd_err, false)
    r = spawn(false, cmd, (pin, cmd_out, cmd_err))
    Base.close_pipe_sync(cmd_out)
    Base.close_pipe_sync(cmd_err)
    Base.close_pipe_sync(pin)
    Base.start_reading(out)
    Base.start_reading(err)
    return (cmd_pin, out, err, r)
end

However, i don't see a clear way to refactor this to use the @tmp_wpipe and @tmp_rpipe macros, or how the work in #11919 will affect this.

@mbaz
Copy link
Contributor Author

mbaz commented Jun 27, 2016

@vtjnash Sorry to resurrect this old issue... however, I get the feeling that 0.5 is getting close to release, and readandwrite() still does not support reading from STDERR, and I don't see any support for this use case in the documentation.

I know about pipeline(), but it only allows redirection to files, but I need to read and write to the pipes directly.

@vtjnash
Copy link
Sponsor Member

vtjnash commented Jun 27, 2016

questions should be asked on julia-users or stack overflow. readandwrite is slated for deprecation as it is redundant with open. also, pipeline has no such restriction as you claim.

But just to make sure my cleanup was successful, I tested the cleaned up version of your above function. Although, if you only need to run a single command, then using open or pipeline provides easier wrappers for the same.

julia> function popen3(cmd::Cmd)
           in = Pipe()
           out = Pipe()
           err = Pipe()
           r = spawn(cmd, (in, out, err))
           return (in, out, err, r)
       end

julia> in, out, err, r = popen3(`ls`)

julia> r = spawn(`ls -l VERSION`, (in, out, err))

julia> r = spawn(`ls -l VERSION`, (in, out, err))

julia> r = spawn(`ls -l VERSION`, (in, out, err))

julia> close(in); close(out.in); close(err.in);

julia> readstring(out)
"CONTRIBUTING.md\nDEBUGGER.md\nDISTRIBUTING.md\nHISTORY.md\nLICENSE.md\nMake.inc\nMake.user\nMakefile\nNEWS.md\nREADME.arm.md\nREADME.md\nREADME.windows.md\nVERSION\nui\nusr\nusr-staging\n-rw-r--r--  1 jameson  staff  10 Nov 10  2015 VERSION\n-rw-r--r--  1 jameson  staff  10 Nov 10  2015 VERSION\n-rw-r--r--  1 jameson  staff  10 Nov 10  2015 VERSION\n"

@mbaz
Copy link
Contributor Author

mbaz commented Jun 28, 2016

Thanks, and sorry for the noise. The source of my problem here is that I don't see a documented way to access stderr from open(), and that both the spawn method you used and Pipe() are also undocumented.

@FlorianFranzen
Copy link

FlorianFranzen commented May 26, 2020

Almost 4 years later, it is still not documented. Could we at least document Pipe, including the fact that calling close(p) on a pipe deletes all input data, while close(p.in) preserves it? That would have saved me a lot of time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
domain:io Involving the I/O subsystem: libuv, read, write, etc.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants