Sign in to follow this  
Followers 0
DaveF

Capture Child STDOUT Stream

17 posts in this topic

I finished an alpha version this morning, I hope to have something to submit to Jon this weekend (hmm, sounds like what I said about default args...) :idiot:

Uses the functionality of CreateProcess to redirect the standard handles.

Sample code & output:

AutoItSetOption("CaptureRunStdout", 1)
Run("net.exe users", @SystemDir)
While 1
    $line = FileReadLine(@RunStdout)
    If @error = -1 Then ExitLoop
    MsgBox(0, "Line read:", $line)
Wend

Posted Image

There's still a question of what should happen if you run a second process before you've read all the output from the first. I think the easiest thing from the implementation side would be to junk the last pipe and its unread output and create a new pipe.

Please let me know your thoughts.

Dave Fulgham


Yes yes yes, there it was. Youth must go, ah yes. But youth is only being in a way like it might be an animal. No, it is not just being an animal so much as being like one of these malenky toys you viddy being sold in the streets, like little chellovecks made out of tin and with a spring inside and then a winding handle on the outside and you wind it up grrr grrr grrr and off it itties, like walking, O my brothers. But it itties in a straight line and bangs straight into things bang bang and it cannot help what it is doing. Being young is like being like one of these malenky machines.

Share this post


Link to post
Share on other sites



Looks like it could only be a RunWait thing, since just using Run could cause so many problems.


Who else would I be?

Share this post


Link to post
Share on other sites

Looks like it could only be a RunWait thing, since just using Run could cause so many problems.

<{POST_SNAPBACK}>

What kind of problems were you thinking of?

RunWait() could cause problems of its own; the redirected STDOUT pipe has a limited size, if the child process writes it full then the child process should block (suspend) and wait until the process on the other end of the pipe (the parent) reads some of the characters out to make room for another write operation. If you've used RunWait() for your child process then your script is waiting for the child to return before continuing (to do a FileRead() etc.) and the child is waiting for your script to read something off the pipe before continuing, so you're in a deadlock...


Yes yes yes, there it was. Youth must go, ah yes. But youth is only being in a way like it might be an animal. No, it is not just being an animal so much as being like one of these malenky toys you viddy being sold in the streets, like little chellovecks made out of tin and with a spring inside and then a winding handle on the outside and you wind it up grrr grrr grrr and off it itties, like walking, O my brothers. But it itties in a straight line and bangs straight into things bang bang and it cannot help what it is doing. Being young is like being like one of these malenky machines.

Share this post


Link to post
Share on other sites

I thought you meant that if you called another exe while already reading the pipe of the original one, that it could cause some major internal code problems.


Who else would I be?

Share this post


Link to post
Share on other sites

#5 ·  Posted (edited)

Dave,

I see what forum I'm in now, so I assume that this is something that doens't exist in AutoIt yet. I have an immediate use for what you are currently working on. Would it be possible to get the code that you are using to use in my script? I simply need to capture one line from STDOUT and basically, it's no more complex than the example you showed earlier. Please let me know. Thanks!

Edited by KenE

Share this post


Link to post
Share on other sites

Dave,

I see what forum I'm in now, so I assume that this is something that doens't exist in AutoIt yet.  I have an immediate use for what you are currently working on.  Would it be possible to get the code that you are using to use in my script?  I simply need to capture one line from STDOUT and basically, it's no more complex than the example you showed earlier.  Please let me know.  Thanks!

Ken Ellefson

<{POST_SNAPBACK}>

I code recreationally at home, I don't have access to my source from where I am now.

In the past I've had to redirect output of a child process to a file and then read that back in from AutoIt, I'll see if I've got an example that I can post...


Yes yes yes, there it was. Youth must go, ah yes. But youth is only being in a way like it might be an animal. No, it is not just being an animal so much as being like one of these malenky toys you viddy being sold in the streets, like little chellovecks made out of tin and with a spring inside and then a winding handle on the outside and you wind it up grrr grrr grrr and off it itties, like walking, O my brothers. But it itties in a straight line and bangs straight into things bang bang and it cannot help what it is doing. Being young is like being like one of these malenky machines.

Share this post


Link to post
Share on other sites

#7 ·  Posted (edited)

Here go, example that gets the state of a service:

Func AssessSvc()
  ; Capture reports of the running state and current config of the service
  ; to a file. Note that the 2nd command uses >> to append output vs. > .
   RunWait(@ComSpec & ' /c c:\windows\system32\sc.exe interrogate WZCSVC > '_
     & @TempDir & '\wzc-state.txt', @SystemDir, @SW_HIDE)
   RunWait(@ComSpec & ' /c c:\windows\system32\sc.exe qc WZCSVC >> '_
     & @TempDir & '\wzc-state.txt', @SystemDir, @SW_HIDE)
  ; Walk through the output file.
   If FileExists(@TempDir & "\wzc-state.txt") Then
    ; Assume that the service is stopped.
      $ISRUNNING = 0
    ; Open file for reading.
      $OURFILE = FileOpen(@TempDir & '\wzc-state.txt', 0)
    ; Loop to read lines from the file.
      While 1
         $OURLINE = FileReadLine($OURFILE)
        ; FileReadLine sets @error to -1 at end-of-file, so break out of the loop
         If @error = -1 Then ExitLoop
        ; If we find STATE and RUNNING then the service is started, so set ISRUNNING
         If (StringInStr($OURLINE, "STATE")) And (StringInStr($OURLINE, "RUNNING")) Then
            $ISRUNNING = 1
         EndIf
        ; On the first call to this function, determine if startup type is already
        ; on demand and adjust so that the checkbox will be checked accordingly
         If (StringInStr($OURLINE, "START_TYPE")) And (Not IsInt($ISMANUAL)) Then
            If StringInStr($OURLINE, "DEMAND_START") Then
               $ISMANUAL = 1
               $NOAUTORUNSVC = 1
            Else
               $ISMANUAL = 0
            EndIf
         EndIf
      Wend
    ; Close the output file.
      FileClose($OURFILE)
   Else
    ; Couldn't find the output report file.
      ErrorMsg("Can't find SVC report file!")
   EndIf
EndFunc  ;==>AssessSvc

Edit: The code above was composed using the SciTE editor w/ Tidy.AU3 . Thanks, JdeB

Edited by DaveF

Yes yes yes, there it was. Youth must go, ah yes. But youth is only being in a way like it might be an animal. No, it is not just being an animal so much as being like one of these malenky toys you viddy being sold in the streets, like little chellovecks made out of tin and with a spring inside and then a winding handle on the outside and you wind it up grrr grrr grrr and off it itties, like walking, O my brothers. But it itties in a straight line and bangs straight into things bang bang and it cannot help what it is doing. Being young is like being like one of these malenky machines.

Share this post


Link to post
Share on other sites

This is cool, and is something I'd love to be able to do, since it removes the reliance on a temporary file or external utility, but I just wonder whether the implementation makes sense. I would've thought some way of redirecting STDOUT to a variable of your choosing would be better.

Note: I'm not saying I have the answer, just thinking out loud.

Share this post


Link to post
Share on other sites

This is cool, and is something I'd love to be able to do, since it removes the reliance on a temporary file or external utility, but I just wonder whether the implementation makes sense. I would've thought some way of redirecting STDOUT to a variable of your choosing would be better.

Note: I'm not saying I have the answer, just thinking out loud.

<{POST_SNAPBACK}>

In C++ one treats the STDIN/STDOUT/STDERR streams in a file like way (same functions to read and write that you'd use for files), so it just seemed natural to carry that over to AutoIt.

If you dumped multiline child output to a variable you'd still have to search in some way for the line of data you wanted to retrieve, where with FileReadLine() you can tell it what line to read from...


Yes yes yes, there it was. Youth must go, ah yes. But youth is only being in a way like it might be an animal. No, it is not just being an animal so much as being like one of these malenky toys you viddy being sold in the streets, like little chellovecks made out of tin and with a spring inside and then a winding handle on the outside and you wind it up grrr grrr grrr and off it itties, like walking, O my brothers. But it itties in a straight line and bangs straight into things bang bang and it cannot help what it is doing. Being young is like being like one of these malenky machines.

Share this post


Link to post
Share on other sites

Dave,

Thanks for the code.  I've tried your example, and several other variations, but no matter what I do, it's not working.  I get the text file created in the Temp directory, but it's always empty.  When I run the exact same command from the command line, I get the result in the text file, as expected.  Maybe it's the app that I'm running?  I have the C source for the app that I'm trying to capture from, if you'd like to see it.

<{POST_SNAPBACK}>

Are you using a command shell to run your app? You have to do so if you're using the > redirect... (cf. my example)

Yes yes yes, there it was. Youth must go, ah yes. But youth is only being in a way like it might be an animal. No, it is not just being an animal so much as being like one of these malenky toys you viddy being sold in the streets, like little chellovecks made out of tin and with a spring inside and then a winding handle on the outside and you wind it up grrr grrr grrr and off it itties, like walking, O my brothers. But it itties in a straight line and bangs straight into things bang bang and it cannot help what it is doing. Being young is like being like one of these malenky machines.

Share this post


Link to post
Share on other sites

#11 ·  Posted (edited)

Here's the code I'm using:

FileChangeDir(EnvGet("LOGONSERVER"), & '\NETLOGON\')

$result=RunWait(@ComSpec & ' /c NTNAME.EXE %USERNAME% > ' & @TempDir & '\OTHERPC.TXT', @SystemDir, @SW_HIDE)

If $result <> 1 Then

Exit

Else

FileChangeDir(EnvGet("TEMP"))

$file = FileOpen("OTHERPC.TXT", 0)

; Check if file opened for reading OK

If $file = -1 Then

Exit

EndIf

; Read in lines of text until the EOF is reached

$line = FileReadLine($file)

If @error = -1 Then

Exit

EndIf

FileClose($file)

Edited by KenE

Share this post


Link to post
Share on other sites

You may need to double quote (") the output file and directory.


Who else would I be?

Share this post


Link to post
Share on other sites

DaveF,

this looks really cool, thanks a lot! However, do you think that it will ever be possible to read the contents of an existing console window or of a console window that is not directly open by autoit?

For instance, let's say that I call a batch file from autoit, or perhaps another autoit executable program and it creates an additional stand alone command window. Would it be possible to read its output after doing a WinActivate(....) for instance? It does not seem so as you are using CreateProcess...

Angel

Share this post


Link to post
Share on other sites

#14 ·  Posted (edited)

In C++ one treats the STDIN/STDOUT/STDERR streams in a file like way (same functions to read and write that you'd use for files), so it just seemed natural to carry that over to AutoIt.

If you dumped multiline child output to a variable you'd still have to search in some way for the line of data you wanted to retrieve, where with FileReadLine() you can tell it what line to read from...

<{POST_SNAPBACK}>

So what about a mix of Run() and FileOpen() then? Maybe something like this:

$fd=RunPipe('net localgroup Administrators', @SystemDir, @SW_HIDE)
if $fd <> -1 Then
    $x = FileReadLine($fd)
    While not @error
        MsgBox(0, 'Line', $x)
        $x = FileReadLine($fd)
    WEnd
EndIf

Maybe it would also be possible to write to this fd with FileWrite then to interact with the program?

EDIT: Just thought about the error code from the program that is run. I don't know if it's possible but maybe RunPipe could return an array consisting of the file descriptors for the program (stdin, stdout, stderr) and a placeholder for the error code? So you could still get the error code this way:

$x=RunPipe('Net localgroup Administrators', @SystemDir, @SW_HIDE)
if not @error Then
    $line = FileReadLine($x[1])
    if not @error Then MsgBox(0, 'First Line', $line)
    FileWriteLine($x[0], 'Some input to send to the program. Well, net localgroups does not need any.')
  ; Now wait for program to end
    FileClose($x[1])
    $errorcode = $x[3]
Endif

I think $x should look like this:

$x[0] = STDIN

$x[1] = STDOUT

$x[2] = STDERR

$x[3] = error code that's returned by the program

Of course $x[3] should never be read before the program exited (that's the reason for the FileClose($x[1]). The script should pause there until the program finished).

I don't know if all of my ideas are possible or even sensible because I have only written very simple programs in C/C++ for now but they're here for discussion.

Edited by sugi

Share this post


Link to post
Share on other sites

Angel, I believe I saw a post regarding interaction with console windows elsewhere in the forum.

Sugi, thanks for the post. After having the day to experiment and review I see that it will be easier to pipe the entirety of the standard output to a string and then access that from a macro like @RunStdout. Reconciling the difference between the HANDLE-based Win32 functions and the FILE* -based standard library functions was causing an ever-growing code bloat and duplication for the desired result. By just copying to output to a string and closing the pipe it won't be an issue if you run another process, @RunStdout just gets updated with the output of each new Run().

Since you do have access to the STDIN pipe through the same method I'm using it would be possible to pipe STDIN to the child process, but it would probably be best to prepare what you wanted to pipe in (like @RunStdin = "blah") before calling Run() because the child process would be paused until the script's end of the STDIN pipe was written to and closed, which would again run a risk of deadlock if you had used RunWait() to run your child process...

STDERR could be captured in its own string exactly the way that STDOUT will be, or both can be directed to the same pipe and string. Options can be added to configure these things if there's enough interest; I may put together a poll about it.


Yes yes yes, there it was. Youth must go, ah yes. But youth is only being in a way like it might be an animal. No, it is not just being an animal so much as being like one of these malenky toys you viddy being sold in the streets, like little chellovecks made out of tin and with a spring inside and then a winding handle on the outside and you wind it up grrr grrr grrr and off it itties, like walking, O my brothers. But it itties in a straight line and bangs straight into things bang bang and it cannot help what it is doing. Being young is like being like one of these malenky machines.

Share this post


Link to post
Share on other sites

If one can only access the output after program's execution and provide the input before, there will be no possibility to interact with the program depending on it's output.

For no-wait runs i would prefer having the chance to act interactively.

For RunWait it is clearly not needed any Output mapped to Variable would be perfect.

I know, from own experience, that it can be a little bit hard to get and transfer the output/input stream without unwanted buffering.

Share this post


Link to post
Share on other sites

I've got the initial version done. It captures both standard output and the standard error streams to a variable during the course of the Run() process. The user can access the variable after the Run() returns to get the output of the child process.

I'm starting a poll to see what functionality would be used if it was present.


Yes yes yes, there it was. Youth must go, ah yes. But youth is only being in a way like it might be an animal. No, it is not just being an animal so much as being like one of these malenky toys you viddy being sold in the streets, like little chellovecks made out of tin and with a spring inside and then a winding handle on the outside and you wind it up grrr grrr grrr and off it itties, like walking, O my brothers. But it itties in a straight line and bangs straight into things bang bang and it cannot help what it is doing. Being young is like being like one of these malenky machines.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0