20

This is a VBScript code example that shows how to catch whatever a command line program sends to standard output. It executes the command xcopy /? and shows the output in a message box. Before the message box appears, for a split second you see the console window popping up.

Set objShell = WScript.CreateObject("WScript.Shell")
Set objExec = objShell.Exec("xcopy /?")
Do
    line = objExec.StdOut.ReadLine()
    s = s & line & vbcrlf
Loop While Not objExec.Stdout.atEndOfStream
WScript.Echo s

Here is an other VBScript code example that shows how to execute a script without showing the console window.

objShell.Run "c:\temp\mybatch.bat C:\WINDOWS\system32\cmd.exe", 0

or

objShell.Run "c:\temp\myscript.vbs C:\WINDOWS\system32\cscript.exe", 0

As you can see it has the form <script><space><executor>. The last example uses objShell.Run instead of objShell.Exec

What I don't know is how to execute a command line program (if necessary from a batch file), catch the standard output, without showing the console window. Any ideas?

Joey
  • 344,408
  • 85
  • 689
  • 683
mgr326639
  • 902
  • 2
  • 10
  • 24
  • You can use `.Exec()` method, without console window flash, temp files and unexpected `WScript.Echo` output muting, check [this answer](https://stackoverflow.com/a/32302212/2165759). – omegastripes Aug 06 '18 at 22:36

7 Answers7

15

I usually use this:

Wscript.echo execStdOut("ping google.com")

Function execStdOut(cmd)
   Dim goWSH : Set goWSH = CreateObject( "WScript.Shell" ) 
   Dim aRet: Set aRet = goWSH.exec(cmd)
   execStdOut = aRet.StdOut.ReadAll()
End Function 

For more advanced commands youc an wrap to comspec (cmd)

my res = execStdOut("%comspec%" & " /c " & """" & "dir /b c:\windows\*.exe" & """" & " && Echo. && Echo finished") 
Jakob Sternberg
  • 159
  • 1
  • 3
  • 2
    Note that this will hang if your program outputs too much (about 4KB IIRC), and it's not hidden. – Camilo Martin Sep 05 '12 at 15:54
  • @CamiloMartin Where are you getting the 4KB limitation from? Is that a limitation of `StdOut.ReadAll()`. I just used `StdOut.ReadLine()` on a 109KB file without issues per http://ss64.com/vb/stdoutread.html. Although I can confirm the "not hidden" observation, making this solution non-ideal for those seeking a "hidden window" as a strict requirement, per the OP. – tresf May 02 '15 at 01:27
  • @QZSupport Honestly I don't have the faintest clue anymore, but probably I tried it and something hung after 4KB, then other two people came and upvoted it because something hung for them too. If it actually works that's great, just make sure to test on all Windows versions you're supporting. (Also, it should be noticed: it is *very likely* `ReadAll` will read everything into RAM, and would be very ugly on its own - I'd say you should stream the StdOut instead, but again I don't know how; `ReadLine` wouldn't be good for binary output). – Camilo Martin May 03 '15 at 14:53
  • @CamiloMartin, good point about the binary and ReadLine(), that could get messy when the CRLFs never make it into the stream. However, I'd like to reiterate the OP's requirement of StdOut, which tends to favor non-binary representations of data. In regards to multiple platforms, I'll perform some testing as far back as XP and post an update. – tresf May 04 '15 at 15:40
  • 2
    @CamiloMartin Edit: I just read 1145 lines on text weighing in at about 160KB and worked just fine on Windows XP SP3 (as long as I remember to use cmd /c and not cmd /k). For reading multi-line output, I see this solution as being perfectly adequate for most people, but I would recommend to use ReadLine() over ReadAll(). In terms of the 4KB limitation, it is explained further here: http://support.microsoft.com/en-us/kb/960246 – tresf May 04 '15 at 18:40
  • @QZSupport - https://support.microsoft.com/en-us/help/960246/hang-when-reading-stderr-stdout-properties-of-wshscriptexec-object talks about the 4kb limit – Pankaj Jaju Feb 20 '17 at 16:07
  • 1
    @PankajJaju thanks. Per that same article `Generally, this occurs if the spawned application is writing to both the StdOut and StdErr streams`. So applications that do not write to both `stderr` AND `stdout` shouldn't suffer this bug. – tresf Feb 21 '17 at 19:52
6

In order to redirect the output to the console, run the script using cscript, ex.: c:\cscript myscript.vbs.

cscript has a few command line options. The most important (to me) is the switch //NOLOGO. If yoy use it (cscript //nologo myscript.vbs) it will omit Microsoft merchandise...

Jeff Puckett
  • 37,464
  • 17
  • 118
  • 167
Bob Rivers
  • 5,261
  • 6
  • 47
  • 59
3

This proof of concept script:

' pocBTicks.vbs - poor man's version of backticks (POC)

Option Explicit

' Globals

Const SW_SHOWMINNOACTIVE =  7
Const ForReading         =  1

Dim goFS  : Set goFS  = CreateObject( "Scripting.FileSystemObject" )
Dim goWSH : Set goWSH = CreateObject( "WScript.Shell" )

' Dispatch
WScript.Quit demoBTicks()

' demoBTicks -
Function demoBTicks()
  demoBTicks = 1
  Dim aCmds : aCmds = Array( _
      "dir pocBTicks.vbs" _
    , "dur pocBTicks.vbs" _
    , "xcopy /?" _
  )
  Dim sCmd
  For Each sCmd In aCmds
      WScript.Echo "########", sCmd
      Dim aRet : aRet = BTicks( sCmd )
      Dim nIdx
      For nIdx = 0 To UBound( aRet )
          WScript.Echo "--------", nIdx
          WScript.Echo aRet( nIdx )
      Next
  Next
  demoBTicks = 0
End Function ' demoBTicks

' BTicks - execute sCmd via WSH.Run
'  aRet( 0 ) : goWSH.Run() result
'  aRet( 1 ) : StdErr / error message
'  aRet( 2 ) : StdOut
'  aRet( 3 ) : command to run
Function BTicks( sCmd )
  Dim aRet    : aRet     = Array( -1, "", "", "" )
  Dim sFSpec2 : sFSpec2  = goFS.GetAbsolutePathName( "." )
  Dim sFSpec1 : sFSpec1  = goFS.BuildPath( sFSpec2, goFS.GetTempName() )
                sFSpec2  = goFS.BuildPath( sFSpec2, goFS.GetTempName() )

  aRet( 3 ) = """%COMSPEC%"" /c """ + sCmd + " 1>""" + sFSpec1 + """ 2>""" +  sFSpec2 + """"""
  Dim aErr
 On Error Resume Next
  aRet( 0 ) = goWSH.Run( aRet( 3 ), SW_SHOWMINNOACTIVE, True )
  aErr      = Array( Err.Number, Err.Description, Err.Source )
 On Error GoTo 0
  If 0 <> aErr( 0 ) Then
     aRet( 0 ) = aErr( 0 )
     aRet( 1 ) = Join( Array( aErr( 1 ), aErr( 2 ), "(BTicks)" ), vbCrLf )
     BTicks    = aRet
     Exit Function
  End If

  Dim nIdx : nIdx = 1
  Dim sFSpec
  For Each sFSpec In Array( sFSpec2, sFSpec1 )
      If goFS.FileExists( sFSpec ) Then
         Dim oFile : Set oFile = goFS.GetFile( sFSpec )
         If 0 < oFile.Size Then
            aRet( nIdx ) = oFile.OpenAsTextStream( ForReading ).ReadAll()
            goFS.DeleteFile sFSpec
         End If
      End If
      nIdx = nIdx + 1
  Next
  BTicks = aRet
End Function

shows how to use .Run and temporary files to get something like backticks with a hidden console. Decent file handling, quoting in sCmd, cleaning of the returned strings, and dealing with encodings will require more work. But perhaps you can use the strategy to implement something that fits your needs.

Ekkehard.Horner
  • 38,498
  • 2
  • 45
  • 96
  • An ingenious solution. Pity about the temporary files. But it meets the requirements so the points are yours. Thanks, Marcel. – mgr326639 Feb 14 '11 at 08:01
1

To return in VBA all subfolders in G:\OF

sub M_snb()
  c00= createobejct("wscript.Shell").exec("cmd /c Dir G:\OF\*. /s/b").stdout.readall
end sub

to split the returned string into an array

sub M_snb()
  sn=split(createobejct("wscript.Shell").exec("cmd /c Dir G:\OF\*. /s/b").stdout.readall,vbCrLf)

  for j=0 to ubound(sn)
     msgbox sn(j)
  next
End Sub
Phil Nicholas
  • 3,681
  • 1
  • 19
  • 23
snb
  • 29
  • 1
1

If you don't mind having the taskbar button appear, you can just move the console window offscreen before launching it.

If the HKCU\Console\WindowPosition key exists, Windows will use its value to position the console window. If the key doesn't exist, you'll get a system-positioned window.

So, save the original value of this key, set your own value to position it offscreen, call Exec() and capture its output, then restore the key's original value.

The WindowPosition key expects a 32-bit value. The high word is the X coordinate and the low word is the Y coordinate (XXXXYYYY).

With CreateObject("WScript.Shell")

    ' Save the original window position. If system-positioned, this key will not exist.
    On Error Resume Next
    intWindowPos = .RegRead("HKCU\Console\WindowPosition")
    On Error GoTo 0

    ' Set Y coordinate to something crazy...
    .RegWrite "HKCU\Console\WindowPosition", &H1000, "REG_DWORD"

    ' Run Exec() and capture output (already demonstrated by others)...
    .Exec(...)

    ' Restore window position, if previously set. Otherwise, remove key...
    If Len(intWindowPos) > 0 Then
        .RegWrite "HKCU\Console\WindowPosition", intWindowPos, "REG_DWORD"
    Else
        .RegDelete "HKCU\Console\WindowPosition"
    End If

End With

If you really want to make sure the coordinates are offscreen, you can get the screen dimensions via VBScript by using IE or other tools.

Bond
  • 16,071
  • 6
  • 30
  • 53
0

This is the way you can get the command line StdOut (result) without see this popup black dos windows in vbscript:

Set Sh = CreateObject("WScript.Shell")
tFile=Sh.ExpandEnvironmentStrings("%Temp%")&"\t.txt"
Sh.Run "cmd.exe /c xcopy /? > """&tFile&""" ",0,False
Wscript.echo CreateObject("Scripting.FileSystemObject").openTextFile(tFile).readAll()
hollopost
  • 569
  • 9
  • 28
-1

Instead of WScript.Shell, consider using using Win32_Process with startupInfo.ShowWindow = 0 to launch the process with SW_HIDE. I posted a detailed example under VBS Run cmd.exe output to a variable; not text file.

Community
  • 1
  • 1
Stephen Quan
  • 21,481
  • 4
  • 88
  • 75
  • Interesting piece of code. But I don't really see the advantage in this case, because you are still relying on a temporary file. – mgr326639 Mar 03 '14 at 12:51