Can I spawn a subprocess in such a way, that its stderr is redirected to parent proc's stderr, whereas stdout can be read normally?
Alternatively: after I run startProcess, can I somehow detect which of outputStream vs. errorStream has some pending data I can read? Or, are there non-blocking versions of read on outputStream & errorStream?
I need to distinguish between stdout vs. stderr, so poStdOutToStdErr is a no-go. Also, I need to support all major desktop platforms (Win, Lin, Mac); if you know a solution for only one of them, I'm still interested - there's still hope somebody may chime-in with a solution for the others later...
perhaps like this
#include <stdio.h>
int main() {
fprintf(stdout, "This is from hello to stdout\n");
fprintf(stderr, "This is from hello to stderr\n");
return 0;
}
and our nim file
import os, osproc, selectors
when defined(windows):
import streams
proc main =
when defined(linux):
let prc = startProcess("./a.out", options = {})
let errh = errorHandle prc
let outh = outputHandle prc
var stor = newSelector[string]()
defer: stor.close()
stor.registerHandle(int errh, {Read}, "")
stor.registerHandle(int outh, {Read}, "")
var tempout, temperr: File
running = tempout.open(outh)
running = temperr.open(errh)
var running = true
while running:
let keys = stor.select -1
for k in keys:
if k.errorCode.int != 0:
echo k.errorCode.osErrorMsg()
running = false
continue
if k.fd == outh:
echo "stdout: ", tempout.readLine
elif k.fd == errh:
echo "stderr: ", temperr.readLine
close prc
else:
echo "on windows"
let prc = startProcess("./a.exe", options = {})
var errstream = errorStream prc
var outstream = outputStream prc
while true:
var data = errstream.readLine
if data == "":
break
else:
echo "stderr: ", data
data = outstream.readLine
if data == "":
break
else:
echo "stdout: ", data
main()
Note that on windows, selectors doesn't work with file, trying to use createIoCompletionPort apparently couldn't work because the pipe is busy, because of this also we cannot use ReOpenFile to enable flag FILE_FLAG_OVERLAPPED. But, we got stream there so we can emulate async with peek proc variants (peekStr and peekLine). cmiiw
Thanks! I didn't check the Linux version yet, but the Windows one doesn't seem to really do what I need: specifically, if I print from a Nim file like this:
# q_outerr.nim
import os
stdout.writeLine "some stdout"
stderr.writeLine "some stderr"
stderr.writeLine "some stderr2"
sleep(milsecs = 5_000)
stderr.writeLine "some stderr3"
stderr.writeLine "some stderr4"
with the windows part of the "reader" file modified like this:
# q_peek.nim
...
else:
echo "on windows"
let prc = startProcess("q_outerr", options = {})
var errstream = errorStream prc
var outstream = outputStream prc
while true:
if errstream.atEnd:
echo "err end"
else:
let edata = errstream.peekLine
if edata != "":
echo "stderr: ", errstream.readLine
if outstream.atEnd:
echo "out end"
else:
let odata = outstream.peekLine
if odata != "":
echo "stdout: ", outstream.readLine
if errstream.atEnd and outstream.atEnd:
echo "-break-"
break
...
I'm getting a SIGSEGV:
on windows
Traceback (most recent call last)
C:\prog\mana\q_peek.nim(61) q_peek
C:\prog\mana\q_peek.nim(48) main
C:\Users\Mateusz\.choosenim\toolchains\nim-1.0.4\lib\pure\streams.nim(979) peekLine
C:\Users\Mateusz\.choosenim\toolchains\nim-1.0.4\lib\pure\streams.nim(213) getPosition
SIGSEGV: Illegal storage access. (Attempt to read from nil?)
Error: execution of an external program failed: 'C:\prog\mana\q_peek.exe '
I also tried readLine instead of peekLine, but then the sleep makes q_peek block until after sleep is finished. I want to be able to discern if there's some stderr available from a not yet finished subprocess...
What do you refer to in: "createIoCompletionPort apparently couldn't work"? couldn't where? did someone try it sometime? I think in Go they maybe managed to somehow make it work?
For now, at least as far as solution for Windows, it seems I'm still looking for help...
The streams implementation on Windows (FileHandleStream to be precise) didn't implement peekDataImpl, setPositionImpl and getPositionImpl hence it crashed because the streams module accessing nil implementation.
I actually fixed this (not PR-ed it yet tho, maybe already fixed in devel too, haven't checked it yet), but the actual problem is another, it's the C buffer that wouldn't flush to stdout/stderr handle until the the subprocess' buffer is full.