除非指定输出文件,否则 VBScript 的 Shell 命令将不会执行

VBScript's Shell Command Will Not Execute Unless An Output File Is Specified

我正在尝试 运行 shell 命令进行 google 语音识别。如果我向命令字符串提供输出文件,我只能 运行 命令

正如您在下面看到我的测试代码示例,如果提供了“>outputFile”,我会附上它,并且还在超时循环中编码以在设定时间限制。

strCommand = "cmd /c ipconfig /all"
If outputFile <> "" Then
    strCommand = strCommand & " > """ & outputFile & """"
End If
Set wshShellExec = wshShell.Exec(strCommand)
expiration = DateAdd("s", 600, Now)
Do While wshShellExec.Status = WshRunning And Now < expiration
    WScript.Sleep 5000
Loop 
Select Case wshShellExec.Status
    Case WshRunning
        wshShellExec.Terminate
        TestFunction = "{""error"": ""TestFunction Command Timed Out""}"
    Case WshFinished
        TestFunction =  WshShellExec.StdOut.ReadAll()
    Case WshFailed
        TestFunction = wshShellExec.StdErr.ReadAll()
End Select

如果我将 outputFile 留空并尝试期望函数返回输出,它所做的只是静置 5 分钟,然后超时并向我发送错误消息。 为什么它需要输出文件到 运行? 如果我在命令提示符下手动 运行 命令行,它 运行 完全没问题。

一旦 wshShellExecWshRunning 情况下终止,而不是分配错误消息,应该分配输出。

Select Case wshShellExec.Status
Case WshRunning
    wshShellExec.Terminate
    TestFunction = "Terminated: " & vbcrlf & WshShellExec.StdOut.ReadAll() 
Case WshFinished
    TestFunction =  "Finished: " & vbcrlf & WshShellExec.StdOut.ReadAll()
Case WshFailed
    TestFunction = wshShellExec.StdErr.ReadAll()
End Select

输出缓冲区容量有限。如果您的命令向 stdout 写入过多文本,缓冲区将填满并阻止命令写入更多文本,直到您清除缓冲区(例如,通过读取缓冲区)。但是,ReadAll 不能用于此,因为该方法只会 return 命令完成后阻塞,否则会造成死锁。

您最好的选择是将输出重定向到一个或多个(临时)文件,并在命令完成后从这些文件中读取输出。

outfile = "C:\out.txt"
errfile = "C:\err.txt"

cmd = "cmd /c ipconfig /all >""" & outfile & """ 2>""" & errfile & """"

timeout = DateAdd("s", 600, Now)

Set sh = CreateObject("WScript.Shell")
Set ex = sh.Exec(cmd)
Do While ex.Status = WshRunning And Now < timeout
    WScript.Sleep 200
Loop

Set fso = CreateObject("Scripting.FileSystemObject")
outtxt = fso.OpenTextFile(outfile).ReadAll
errtxt = fso.OpenTextFile(errfile).ReadAll

如果您出于某种原因不想这样做,您必须反复阅读 StdOut

outtxt = ""
errtxt = ""

cmd = "ipconfig /all"

timeout = DateAdd("s", 600, Now)

Set sh = CreateObject("WScript.Shell")
Set ex = sh.Exec(cmd)
Do While ex.Status = WshRunning And Now < timeout
    WScript.Sleep 200
    outtxt = outtxt & ex.StdOut.ReadLine & vbNewLine
Loop

请注意,您可能还需要从 StdErr 读取数据,因为如果错误输出过多,该缓冲区也可能会填满。但是,读取 both 缓冲区可能会创建 another 死锁,因为 IIRC ReadLine 阻塞直到它可以读取整行,所以如果脚本可能会挂起等待永远不会出现的错误输出。您可以使用 Read 而不是 ReadLine 来解决这个问题,但它仍然非常脆弱。

因此,最好的选择还是将命令输出重定向到文件,并在命令终止后读取这些文件。