We help IT Professionals succeed at work.

Retrieve Output from External Program with vbscript

abgtemp
abgtemp asked
on
I'm using a vbscript to run an external batch script. I am using the following code to process the output of the batch script so it can be view in the same window. The only problem that I am having is that the following code only pulls the output after the batch script completes. I'm trying to figure out how to pull the output while the batch script is running. I tried using stdout.readline, but it only returns the first line.
Function Run (ByVal cmd)
   Dim sh: Set sh = CreateObject("WScript.Shell")
   Dim wsx: Set wsx = Sh.Exec(cmd)
   If wsx.ProcessID = 0 And wsx.Status = 1 Then     
      Err.Raise vbObjectError,,"WshShell.Exec failed."
      End If
   Do
      Dim Status: Status = wsx.Status
      WScript.StdOut.Write wsx.StdOut.ReadAll()
      WScript.StdErr.Write wsx.StdErr.ReadAll()
      If Status <> 0 Then Exit Do
      WScript.Sleep 10
      Loop

   Run = wsx.ExitCode
   End Function

' Runs an internal command interpreter command.
Function RunCmd (ByVal cmd)
RunCmd = Run("%ComSpec% /c " & cmd)
End Function

return = run("c:\myscript.bat")

Open in new window

Comment
Watch Question

Most Valuable Expert 2012
Top Expert 2014
Commented:
Hi, this works for me, but I can't understand why it doesn't work when you try to echo the StdErr output.

Rob.
Function Run (ByVal cmd)
	Set objShell = CreateObject("WScript.Shell")
	Set objExec = objShell.Exec(cmd)
	If objExec.ProcessID = 0 And objExec.Status = 1 Then     
		Err.Raise vbObjectError,,"WshShell.Exec failed."
	End If
	While objExec.Status = 0
		WScript.Echo objExec.StdOut.ReadLine()
	Wend
	Run = objExec.ExitCode
End Function

return = run("c:\temp\scripts\TestBAT1.bat")
WScript.Echo "Return: " & return

Open in new window

Author

Commented:
Yeah. Not sure why that didn't work either. But you solution worked like a charm. Thanks. I am doubling the points for the quick and precise solution.
Most Valuable Expert 2012
Top Expert 2014

Commented:
Great!  Thanks for the extra points and the grade.

Rob.

Author

Commented:
Is there a way to get both StdOut and StdErr? When I try to also capture StdErr, it pretty much skips all of the StdOut lines

While objExec.Status = 0
            WScript.Echo objExec.StdOut.ReadLine()
                WScript.Echo objExec.StdErr.ReadLine()
Wend
Most Valuable Expert 2012
Top Expert 2014

Commented:
I was thinking about this last night.  I think what the ReadLine method might be doing is actually waiting for a line to be available for reading from the StdErr object.  If the program runs normally, then this will never continue.

I still couldn't figure out why it won't allow the outputs to be read by lines.  If you ReadAll on both, outside the loop, it works fine.  Anyway, what I have come up with is a bit of hack, whereby you run the batch file in a command prompt, and redirect *its* standard error back to standard output, which the VBS can read fine.

So you have to add cmd /c to the start of the command, and 2>&1 to the end.

Regards,

Rob.
Function Run (ByVal cmd)
	Set objShell = CreateObject("WScript.Shell")
	Set objExec = objShell.Exec(cmd)
	If objExec.ProcessID = 0 And objExec.Status = 1 Then     
		Err.Raise vbObjectError,,"WshShell.Exec failed."
	End If
	While objExec.Status = 0
		If Not objExec.StdOut.AtEndOfStream Then WScript.Echo objExec.StdOut.ReadLine()
	Wend
	Run = objExec.ExitCode
End Function

return = run("cmd /c c:\temp\scripts\TestBAT1.bat 2>&1")
WScript.Echo "Return: " & return

Open in new window

Author

Commented:
Thanks!! I guess we were thinking along the same lines. I had tested using  2>&1 before I saw your answer and that worked. The part that I am now stuck on now is my batch script runs a process that could possibly ask the user for a response. I want to kill the script if that is the case since the script would just sit there forever. I thought that capturing the stderr would allow me to check for that specific question that it may ask and then I could kill it, but it looks like I may have to also capture the stdin to do this. Is it possible to redirect stdin and stderr to stdout? Or is that the correct approach?
Most Valuable Expert 2012
Top Expert 2014

Commented:
What if you do this:
return = run("cmd /c echo test|c:\temp\scripts\TestBAT1.bat 2>&1")

and then look for the string "test" to determine that that user input was verified?

Otherwise, if you use a timer within the loop, you could say that if there was no input for five seconds, to have it exit? Does that work?

Rob.

Author

Commented:
Thanks. I may have to go with the timer. I'm more concern in trying to capture the question being asked versus the actual input. In this case, there would never be a user input since this will be ran remotely. What the batch script is actually running is a backup process. If it is a new backup, the backup client will ask for the client's password to be entered to login to the backup server. I was trying to capture that question so I could kill the script if that was the case or just have the script provide the password. I may just open another question concerning this since this is going into a slightly different subject.

Author

Commented:
I actually have gotten a little closer.  By using the following, I am able to capture the question being asked to a file. I would rather have this also directed to screen. But if I can't figure that out, I guess I will have to do a check on the file.

return = run("cmd /c c:\temp\scripts\TestBAT1.bat >&3 2>c:\mytempfile.txt")

Author

Commented:
I think I realized my problem. The issue is not with the redirection, so just using 2>&1 will actually give me the output I'm looking for. The problem I think is with the stdout.readline.  I'm assuming readline is waiting for the newline feed or character before passing that line to screen. Since the line that asks the question and waits for user input does not return a newline character, it would never be displayed on screen. When using stdout.read(1), I am able to see the question being asked, but every character is just on a single line. So just trying to go from here now.
Most Valuable Expert 2012
Top Expert 2014

Commented:
Hi, since this question is now closed, if you post a "related question", we'll get some other people to look into it too.  I guess the only problem with reading the characters one by one is that you'll need to check for the existence of a sentence with each character being read, but I guess that wouldn't pose much problem.  It's just less efficient.

Rob.