Jump to content

RunReadStd


lod3n
 Share

Recommended Posts

I've had a personal UDF similar to this one for some time that I was fond of, which only returned the stdout and stderr text from a command, because it's a hassle to set it up every time you want capture stdout data. And then MHz's _ProcessOpenHandle() and _ProcessGetExitCode() hit the forum, and I just had to incorporate that functionality. I think it's pretty useful at this point, so I thought I'd share it.

Just be aware that it returns the stdout and stderr values with blank lines removed and tab delimited, so if that's not something you feel like dealing with, well, then change it.

No includes are required. Also, it (necessarily) waits until the command is finished, similar to RunWait. Below I've written another function checkNet, which utilizes the UDF.

Let me know what you think, and please point out any bugs that I missed.

Edit: Added mrRevoked's changes, so you can pick a delimiter other than @tab, and optionally have it return one of the items in the array, instead of the array itself. Much more flexible, thanks mrRevoked.

Edit: ptrex pointed out that it wouldn't run tasklist. It seemed to be hanging on the While ProcessExists loop, though I don't know why. So I took that out, and placed the exit code determination after the stdout and stderr reads. It runs now, and quite a bit faster too!

Edit: Updated for 3.2.10.0. (Hey thanks for the kudos, flyingboz!)

;example single item return mode usage - by mrRevoked
MsgBox(0,'Ipconfig...',_RunReadStd("ipconfig",0,@WorkingDir,0,1,@LF))

; example array mode usage
if checknet("localhost") then 
    ; do stuff!
EndIf



func checknet($server)
    ConsoleWrite("Active network adapter?... ")
    local $aOutput = _RunReadStd("ipconfig")
    
    ;$aOutput[1] now contains the StdOut from ipconfig, so the idea here is that
    ;if your network cable is unplugged, ipconfig will not list a subnet mask:
    if not stringinstr($aOutput[1],"255.") then 
        ConsoleWrite("FAIL" & @crlf)
        return false
    Else
        ConsoleWrite("PASS" & @crlf)
    EndIf
        
    ConsoleWrite("Checking server connection... ")
    if not ping($server) then 
        ConsoleWrite("FAIL" & @crlf)
        return False
    Else
        ConsoleWrite("PASS" & @crlf)
    EndIf
        
    return true
EndFunc



;===============================================================================
;
; Function Name:   _RunReadStd()
;
; Description::    Run a specified command, and return the Exitcode, StdOut text and 
;                  StdErr text from from it. StdOut and StdErr are @tab delimited,
;                  with blank lines removed.
;
; Parameter(s):    $doscmd: the actual command to run, same as used with Run command
;                  $timeoutSeconds: maximum execution time in seconds, optional, default: 0 (wait forever),
;                  $workingdir: directory in which to execute $doscmd, optional, default: @ScriptDir
;                  $flag: show/hide flag, optional, default: @SW_HIDE
;                  $sDelim: stdOut and stdErr output deliminter, optional, default: @TAB
;                  $nRetVal: return single item from function instead of array, optional, default: -1 (return array)
;
; Requirement(s):  AutoIt 3.2.10.0
;
; Return Value(s): An array with three values, Exit Code, StdOut and StdErr
;
; Author(s):       lod3n 
;                  (Thanks to mrRevoked for delimiter choice and non array return selection)
;                  (Thanks to mHZ for _ProcessOpenHandle() and _ProcessGetExitCode())
;                  (MetaThanks to DaveF for posting these DllCalls in Support Forum)
;                  (MetaThanks to JPM for including CloseHandle as needed) 
;
;===============================================================================

func _RunReadStd($doscmd,$timeoutSeconds=0,$workingdir=@ScriptDir,$flag=@SW_HIDE,$nRetVal = -1, $sDelim = @TAB)
    local $aReturn,$i_Pid,$h_Process,$i_ExitCode,$sStdOut,$sStdErr,$runTimer
    dim $aReturn[3]
    
    ; run process with StdErr and StdOut flags
    $runTimer = TimerInit()
    $i_Pid = Run($doscmd, $workingdir, $flag, 6) ; 6 = $STDERR_CHILD+$STDOUT_CHILD
    
    ; Get process handle
    sleep(100) ; or DllCall may fail - experimental
    $h_Process = DllCall('kernel32.dll','ptr', 'OpenProcess','int', 0x400,'int', 0,'int', $i_Pid)
    
    ; create tab delimited string containing StdOut text from process
    $aReturn[1] = ""
    $sStdOut = ""
    While 1
        $sStdOut &= StdoutRead($i_Pid)
        If @error Then ExitLoop
    Wend
    $sStdOut = StringReplace($sStdOut,@cr,@tab)
    $sStdOut = StringReplace($sStdOut,@lf,@tab)
    $aStdOut = StringSplit($sStdOut,@tab,1)
    for $i = 1 to $aStdOut[0]
        $aStdOut[$i] = StringStripWS($aStdOut[$i],3)
        if StringLen($aStdOut[$i]) > 0 then 
            $aReturn[1] &= $aStdOut[$i] & $sDelim 
        EndIf
    Next
    $aReturn[1] = StringTrimRight($aReturn[1],1)
    
    ; create tab delimited string containing StdErr text from process
    $aReturn[2] = ""
    $sStderr = ""
    While 1
        $sStderr &= StderrRead($i_Pid)
        If @error Then ExitLoop
    Wend
    $sStderr = StringReplace($sStderr,@cr,@tab)
    $sStderr = StringReplace($sStderr,@lf,@tab)
    $aStderr = StringSplit($sStderr,@tab,1)
    for $i = 1 to $aStderr[0]
        $aStderr[$i] = StringStripWS($aStderr[$i],3)
        if StringLen($aStderr[$i]) > 0 then 
            $aReturn[2] &= $aStderr[$i] & $sDelim 
        EndIf
    Next
    $aReturn[2] = StringTrimRight($aReturn[2],1)
    
    ; kill the process if it exceeds $timeoutSeconds
    if $timeoutSeconds > 0 Then
        if TimerDiff($runTimer)/1000 > $timeoutSeconds Then
            ProcessClose($i_Pid)
        EndIf
    EndIf

    ; fetch exit code and close process handle
    If IsArray($h_Process) Then
        Sleep(100) ; or DllCall may fail - experimental
        $i_ExitCode = DllCall('kernel32.dll','ptr', 'GetExitCodeProcess','ptr', $h_Process[0],'int*', 0)
        if IsArray($i_ExitCode) Then
            $aReturn[0] = $i_ExitCode[2]
        Else
            $aReturn[0] = -1
        EndIf
        Sleep(100) ; or DllCall may fail - experimental
        DllCall('kernel32.dll','ptr', 'CloseHandle','ptr', $h_Process[0])
    Else
        $aReturn[0] = -2
    EndIf

    ; return single item if correctly specified with with $nRetVal
    If $nRetVal <> -1 And $nRetVal >= 0 And $nRetVal <= 2 Then Return $aReturn[$nRetVal]

    ; return array with exit code, stdout, and stderr
    return $aReturn
EndFunc

Changed post title at herewasplato's request to assist in finding it via forum search.

Edited by lod3n

[font="Fixedsys"][list][*]All of my AutoIt Example Scripts[*]http://saneasylum.com[/list][/font]

Link to comment
Share on other sites

nice script..

i modified just a bit, nothing interesting just so you can have it return a specific value, in place of the array.

edit added optional delim value.

MsgBox(0,'Ipconfig...',_RunReadStd("ipconfig",0,@WorkingDir,0,1,@LF))


;===============================================================================
;
; Function Name:   _RunReadStd()
;
; Description:: Run a specified command, and return the Exitcode, StdOut text and
;                 StdErr text from from it. StdOut and StdErr are @tab delimited,
;                 with blank lines removed.
;
; Parameter(s): $doscmd: the actual command to run, same as used with Run command
;                 $timeoutSeconds: maximum execution time in seconds, optional, default: 0 (wait forever),
;                 $workingdir: directory in which to execute $doscmd, optional, default: @ScriptDir
;                 $flag: show/hide flag, optional, default: @SW_HIDE
;
; Requirement(s):  AutoIt 3.1.1
;
; Return Value(s): An array with three values, Exit Code, StdOut and StdErr
;
; Author(s):       lod3n
;                 (Thanks to mHZ for _ProcessOpenHandle() and _ProcessGetExitCode())
;                 (MetaThanks to DaveF for posting these DllCalls in Support Forum)
;                 (MetaThanks to JPM for including CloseHandle as needed)
;
;===============================================================================

func _RunReadStd($sDoscmd, $nTimeoutSeconds = 0, $sWorkingdir = @ScriptDir, $nFlag = @SW_HIDE, $nRetVal = -1, $sDelim = @TAB)
    local $aReturn,$i_Pid,$h_Process,$i_ExitCode,$sStdOut,$sStdErr,$runTimer
    dim $aReturn[3]
   
   ; run process with StdErr and StdOut flags
    $runTimer = TimerInit()
    $i_Pid = Run($sDoscmd, $sWorkingdir, $nFlag, 6); 6 = $STDERR_CHILD+$STDOUT_CHILD
   
   ; Get process handle
    sleep(100); or DllCall may fail - experimental
    $h_Process = DllCall('kernel32.dll','ptr', 'OpenProcess','int', 0x400,'int', 0,'int', $i_Pid)
   
   ; wait for process to exit, or kill it if it exceeds $timeoutSeconds
    While ProcessExists($i_Pid)
        Sleep(100)
        if $ntimeoutSeconds > 0 Then
            if TimerDiff($runTimer)/1000 > $ntimeoutSeconds Then
                ProcessClose($i_Pid)
            EndIf
        EndIf
    WEnd

   ; fetch exit code and close process handle
    If IsArray($h_Process) Then
        Sleep(100); or DllCall may fail - experimental
        $i_ExitCode = DllCall('kernel32.dll','ptr', 'GetExitCodeProcess','ptr', $h_Process[0],'int_ptr', 0)
        If IsArray($i_ExitCode) Then
            $aReturn[0] = $i_ExitCode[2]
        Else
            $aReturn[0] = -1
        EndIf
        Sleep(100); or DllCall may fail - experimental
        DllCall('kernel32.dll','ptr', 'CloseHandle','ptr', $h_Process[0])
    Else
        $aReturn[0] = -2
    EndIf
   
   ; create tab delimited string containing StdOut text from process
    $aReturn[1] = ""
    $sStdOut = ""
    While 1
        $sStdOut &= StdoutRead($i_Pid)
        If @error Then ExitLoop
    Wend
    $sStdOut = StringReplace($sStdOut,@cr,$sDelim)
    $sStdOut = StringReplace($sStdOut,@lf,$sDelim)
    $aStdOut = StringSplit($sStdOut,$sDelim,1)
    for $i = 1 to $aStdOut[0]
        $aStdOut[$i] = StringStripWS($aStdOut[$i],3)
        if StringLen($aStdOut[$i]) > 0 then
            $aReturn[1] &= $aStdOut[$i] & $sDelim
        EndIf
    Next
    $aReturn[1] = StringTrimRight($aReturn[1],1)
   
   ; create tab delimited string containing StdErr text from process
    $aReturn[2] = ""
    $sStderr = ""
    While 1
        $sStderr &= StderrRead($i_Pid)
        If @error Then ExitLoop
    Wend
    $sStderr = StringReplace($sStderr,@cr,$sDelim)
    $sStderr = StringReplace($sStderr,@lf,$sDelim)
    $aStderr = StringSplit($sStderr,$sDelim,1)
    for $i = 1 to $aStderr[0]
        $aStderr[$i] = StringStripWS($aStderr[$i],3)
        if StringLen($aStderr[$i]) > 0 then
            $aReturn[2] &= $aStderr[$i] & $sDelim
        EndIf
    Next
    $aReturn[2] = StringTrimRight($aReturn[2],1)
   
   ; if the process is somehow still running, close it - experimental
    ProcessClose($i_Pid)

;if nreturn <> -1 then return a that specific value.
    If $nRetVal <> -1 And $nRetVal >= 0 And $nRetVal <= 2 Then Return $aReturn[$nRetVal]
    
   ; return array with exit code, stdout, and stderr
    return $aReturn
EndFunc
Edited by mrRevoked
Don't bother, It's inside your monitor!------GUISetOnEvent should behave more like HotKeySet()
Link to comment
Share on other sites

Hi,

nice one. I like it. Maybe you'dlike to change this:

Global $sStdOut = "Hello " & @CR & @CRLF & " World" & @LF
Global $s1 = $sStdOut
ConsoleWrite("1: " & $sStdOut & @CRLF)
$sStdOut = StringReplace($sStdOut, @CR, @TAB)
$sStdOut = StringReplace($sStdOut, @LF, @TAB)
ConsoleWrite("2: " & $sStdOut & @CRLF)
$s1 = StringRegExpReplace($s1, "[\r\n]", @TAB)
ConsoleWrite("3: " & $s1 & @CRLF)

So long,

Mega

Scripts & functions Organize Includes Let Scite organize the include files

Yahtzee The game "Yahtzee" (Kniffel, DiceLion)

LoginWrapper Secure scripts by adding a query (authentication)

_RunOnlyOnThis UDF Make sure that a script can only be executed on ... (Windows / HD / ...)

Internet-Café Server/Client Application Open CD, Start Browser, Lock remote client, etc.

MultipleFuncsWithOneHotkey Start different funcs by hitting one hotkey different times

Link to comment
Share on other sites

@th.meger, I originally tried something like that, but in my testing, I found that in the output there were sometimes @CRs without @LFs, so I couldn't reliably handle that unless I dealt with the two characters separately. I may be wrong though... I will work on a nice fast regex replacement for that part that makes both of us happy.

@GtaSpider, your script is neat too, and it's very much like my own original personal UDF. Grabbing the exit code was complex enough though that I thought it might be useful to more people than just me.

@Reaper HGN and @baghenamoth: thanks and you're welcome.

Edited by lod3n

[font="Fixedsys"][list][*]All of my AutoIt Example Scripts[*]http://saneasylum.com[/list][/font]

Link to comment
Share on other sites

  • 2 months later...
  • 10 months later...

This is just a general purpose stdout/stderr/exit code capturer. It does work with tasklist (now), but that doesn't mean it's a good idea to do so, it just means that it can. Use at your own discretion. :)

3.2.10.0 crashes when I try to run it, even after fixing the 'long_ptr' thing...

Link to comment
Share on other sites

3.2.10.0 crashes when I try to run it, even after fixing the 'long_ptr' thing...

insufficient data to debug. Rest assured that I am using a variant of this effectively with the latest releases w/o issues.

Quick Note: _RunReadStd() is an indispensable part of my everyday toolkit. I just noticed I hadn't weighed in this thread to express my appreciation for this code publicly.

Edited by flyingboz

Reading the help file before you post... Not only will it make you look smarter, it will make you smarter.

Link to comment
Share on other sites

I copy/paste the OP, and get the following after executing:

!>18:03:37 AutoIT3.exe ended.rc:-1073741819

insufficient data to debug.

version? beta?

o/s s/p?

how are you calling the function.

Quit whining, and take ownership for your problem. give us enough info to help.

Don't assume that everyone has your environment, or is interested in duplicating your environment for you.

Give us the above, show us how you're calling it, and you _might_ get a useful response.

Reading the help file before you post... Not only will it make you look smarter, it will make you smarter.

Link to comment
Share on other sites

3.2.10.0, XP SP2, copy/paste/F5

Anyway, it don't matter...found a 5 liner that works just fine...

#include <Array.au3>
$result = Run(@ComSpec & " /c ipconfig", "", @SW_HIDE ,$STDERR_CHILD + $STDOUT_CHILD)   
$line = StdoutRead($result) ; capture output string     Capture the resulting output string to a variable $result
$result_array = StringSplit($line, @LF) ; Split string at line boundaries   Split the string into lines and save a an array
_ArrayDisplay($result_array, "IPconfig")
Link to comment
Share on other sites

I made the one change indicated by the runtime warning, and get expected output.

@OldSchool

I expect that you misread the help file and went mucking around with other values

that you should have left alone.

While the code you show will provide access to stdout, it is nowhere near as flexible or useful as _RunReadStd()

If nothing else, perhaps you have learned what information is necessary to get useful help on this or any other forum.

Best regards.

Edited by flyingboz

Reading the help file before you post... Not only will it make you look smarter, it will make you smarter.

Link to comment
Share on other sites

  • 7 months later...

Thanks, works perfectly. :mellow:

I'll get alot of use out of this one.

I do have one suggestion.

Set @error to 1 if the program couldn't be executed?

I mean I can of course check by seeing if row 1 and 2 are null, but still would be a nice feature :(

Thanks,

Kenny

 "I believe that when we leave a place, part of it goes with us and part of us remains... Go anywhere, when it is quiet, and just listen.. After a while, you will hear the echoes of all our conversations, every thought and word we've exchanged.... Long after we are gone our voices will linger in these walls for as long as this place remains."

Link to comment
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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...