Redirecting stderr on windows


#1

I need to capture stdout and stderr of a win32 program i execute (either using open or spawn). I’ve followed the recomendation in #11824 and tried following:

 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(`net file 0`)

The command net file 0 when executed from commandline (cmd.exe) as “net file 0 2>err.txt” produces stderr output into the file. However, if I do the recomended steps in Julia, pipes are empty.
If I run

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

julia hangs on the command close(err.in) and cannot be interrupted by ctrl-c.
if I skip the closing commands and try

julia> readstring(err)

julia hangs again


i have the feeling that the whole stream redirection is not functional under windows. Even when i try

julia> out, r = open(`net /?`)

The syntax of this command is:

NET
    [ ACCOUNTS | COMPUTER | CONFIG | CONTINUE | FILE | GROUP | HELP |
      HELPMSG | LOCALGROUP | PAUSE | SESSION | SHARE | START |
      STATISTICS | STOP | TIME | USE | USER | VIEW ]
(Pipe(closed => open, 0 bytes waiting),Process(`net /?`, ProcessExited(1)))

The output gets printed on REPL instead of being redirected to the out Pipe.
What am I doing wrong?

Petr


#2

While I haven’t done extensive testing on Windows, stream redirection does seem to work. I use it in my plotting package Gaston, where I need stdin, stderr, and stdout pipes to communicate with gnuplot. See my version of popen3() here. It seems to work in both Julia 0.4 and 0.5.

Actually reading and writing to/from the pipes is slightly more complicated, because these operations are blocking. What I do in Gaston is to set up tasks that can block without interrupting the rest of the program. See the code from here to line 52. The tasks are started only once I know the pipes are up by using this command. Note that the tasks exit when they notice that the pipes have closed.

I hope this can get you going in the right direction. Getting this part of Gaston working was harder than all the rest of the package, and I’m still not 100% certain I’m doing it correctly.

– mb


#3

Thank you very much for your advice. Your solution works great.
I’ve simplified the working example to launch the task only for the nescessary time:

function popen3(cmd::Cmd)
    pin = Base.Pipe()
    out = Base.Pipe()
    err = Base.Pipe()
    r = spawn(cmd, (pin, out, err))
    Base.close_pipe_sync(out.in)
    Base.close_pipe_sync(err.in)
    Base.close_pipe_sync(pin.out)
    Base.start_reading(out.out)
    Base.start_reading(err.out)
    return (pin.in, out.out, err.out, r)
end

pr = popen3(`c:\\windows\\system32\\ping 127.0.0.1 -t`)

function readresult(pr)
    results = [""]
    @sync begin            
        @async begin
            #global results            
            results[1] = ascii(String(readavailable(pr[2])))
        end
    end
    results[1]
end

Calling readresult gives the right output. Note, the results string must be =[""]. If it is ="", the example doesn’t work.

Please could somebody explain me, how to get any results from a task - specifically how the valiables get scoped with the @async block. The manual mentions that all variables are surrounded by a let x=x expressions.
So far my example only works with the string encapsulated ina an array to be a reference.
is there a preferred way to get results from the @async block other than via Arrays?

Thank you
Petr


#4

It can also be done with global variables. This is inelegant but simple, and that’s what I’m doing in Gaston. My understanding is that, at least for the development version of Julia, the preferred way to do this is with channels – see for instance https://github.com/JuliaLang/julia/issues/17699.