捕获stdout的VBScript代码,不显示控制台窗口

20

这是一个VBScript代码示例,演示如何捕获命令行程序发送到标准输出的所有内容。它执行命令xcopy /?并在消息框中显示输出。在消息框出现之前,会短暂地看到控制台窗口弹出。

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

这里有另一个 VBScript 代码示例,展示了如何在不显示控制台窗口的情况下执行脚本。

objShell.Run "c:\temp\mybatch.bat C:\WINDOWS\system32\cmd.exe", 0
或者
objShell.Run "c:\temp\myscript.vbs C:\WINDOWS\system32\cscript.exe", 0

正如您所看到的,它的形式是<script><space><executor>。 最后一个示例使用objShell.Run而不是objShell.Exec

我不知道如何执行一个命令行程序(必要时从批处理文件中),捕获标准输出,而不显示控制台窗口。 有什么想法吗?


你可以使用.Exec()方法,无需控制台窗口闪烁、临时文件和意外的WScript.Echo输出静音,查看此答案 - omegastripes
7个回答

15

我通常使用这个:

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 

对于更高级的命令,你可以包装到 comspec(cmd) 中

my res = execStdOut("%comspec%" & " /c " & """" & "dir /b c:\windows\*.exe" & """" & " && Echo. && Echo finished") 

2
请注意,如果您的程序输出太多(大约4KB左右),它将会挂起,并且不会被隐藏。 - Camilo Martin
@CamiloMartin,你从哪里得到了4KB的限制?这是StdOut.ReadAll()的限制吗?根据http://ss64.com/vb/stdoutread.html,我刚刚在一个109KB的文件上使用了`StdOut.ReadLine()`而没有出现问题。虽然我可以确认“不隐藏”的观察结果,但对于那些严格要求“隐藏窗口”的人来说,这个解决方案并不理想,这也是根据OP的要求。 - tresf
@QZSupport 老实说,我已经一点头绪都没有了,但可能我尝试过,然后在4KB后出现了卡顿,然后另外两个人也遇到了同样的问题并给予了赞同。如果它真正起作用,那太好了,只要确保在支持的所有Windows版本上进行测试即可。(此外,应该注意:很可能ReadAll会将所有内容读入RAM中,并且单独使用会非常丑陋 - 我建议您应该流式传输StdOut,但我不知道如何操作;ReadLine对于二进制输出也不是很好)。 - Camilo Martin
2
@CamiloMartin 编辑:我刚刚读了大约160KB的1145行文本,并在Windows XP SP3上正常工作(只要我记得使用cmd /c而不是cmd /k)。对于阅读多行输出,我认为这个解决方案对于大多数人来说已经足够了,但我建议使用ReadLine()而不是ReadAll()。关于4KB限制,可以在这里进一步了解:http://support.microsoft.com/en-us/kb/960246 - tresf
1
@PankajJaju 谢谢。根据同一篇文章,“一般来说,如果生成的应用程序同时向StdOut和StdErr流写入,则会发生这种情况”。因此,不会向stderrstdout都写入内容的应用程序不应遭受此错误。 - tresf
显示剩余2条评论

6
为了将输出重定向到控制台,请使用cscript运行脚本,例如:c:\cscript myscript.vbs
cscript有一些命令行选项。最重要的(对我来说)是开关//NOLOGO。如果你使用它(cscript //nologo myscript.vbs),它会省略微软的商标...

3
这个概念证明脚本:
' 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

本文演示如何使用.Run 和临时文件来实现类似于隐藏控制台的反引号操作。良好的文件处理、sCmd 中的引号、返回字符串的清理以及编码处理需要更多的工作。但也许您可以使用这种策略来实现适合您需求的功能。


一个巧妙的解决方案。可惜有临时文件。但它满足要求,所以分数归你了。谢谢,马塞尔。 - mgr326639

1
如果您不介意任务栏上出现“任务栏按钮”,则可以在启动之前将控制台窗口移出屏幕。
如果存在“HKCU\Console\WindowPosition”键,则Windows将使用其值来定位控制台窗口。如果键不存在,则会得到一个系统定位的窗口。
因此,保存此键的原始值,设置自己的值以将其定位到屏幕外,调用“Exec()”并捕获其输出,然后恢复键的原始值。
“WindowPosition”键需要一个32位值。高位是X坐标,低位是Y坐标(“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

如果您真的想确保坐标在屏幕外,可以通过使用IE或其他工具通过VBScript获取屏幕尺寸来实现。

但它仍然在任务栏中显示图标,该怎么办? - gao.xiangyang

1

在VBA中返回G:\OF下的所有子文件夹

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

将返回的字符串分割成数组。
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

4
针对以下问题:-1 for: createobejctcmd之前缺少引号,并且没有解决“hidden”规范的问题。翻译:报错原因有三:一是拼写错误,应该是createobject而不是createobejct;二是缺少引号,需要在cmd之前添加引号;三是未满足“hidden”规范的要求。 - Ekkehard.Horner

0
这是在VBScript中获取命令行StdOut(结果)而不看到弹出的黑色DOS窗口的方法:
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()

-1

有趣的代码片段。但在这种情况下,我并没有真正看到优势,因为您仍然依赖于临时文件。 - mgr326639

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接