Board logo

标题: [vbs]截获cmd命令输出的脚本 [打印本页]

作者: electronixtar     时间: 2006-6-7 17:13    标题: [vbs]截获cmd命令输出的脚本

搞了很久才发现WScript.Shell有这么好的一个功能!
Set objShell = CreateObject("WScript.Shell")
Set objExecObject = objShell.Exec ("%comspec% /c " & wscript.arguments(0))
Do While Not objExecObject.StdOut.AtEndOfStream
strText = objExecObject.StdOut.ReadAll()
loop
msgbox strText
保存为GetStdOut.vbs,在命令行里转到相应目录运行:
cscript.exe GetStdOut.vbs "ping 127.1"

看看,是不是出来了:
Pinging 127.0.0.1 with 32 bytes of data:

Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128

Ping statistics for 127.0.0.1:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms
[ Last edited by electronixtar on 2006-6-7 at 17:19 ]
作者: bagpipe     时间: 2006-6-7 17:41
其实对于vbs脚本来说管道的操作还是有一定必要的,而WSCRIPT.SHELL和用EXEC执行的管道命令的属性和方法还是有一定区别的,不过大体一样
作者: electronixtar     时间: 2006-6-8 21:47
BagPipe换头像和签名了啊~~~呵呵~~
作者: willsort     时间: 2006-6-9 03:23
Re electronixtar:

      关于这个主题,下面的链接文章中讲的很详细。

引自:Microsoft TechNet:脚本漫谈--2002年10月:从WSH脚本中运行程序
http://www.microsoft.com/china/t ... /scripts/sg1002.asp


脚本漫谈--2002年10月
从WSH脚本中运行程序
脚本专家组



脚本专家组衷心欢迎您访问我们新近开辟的这个每月一期的专栏。我们原本希望依靠自身力量创办这个专栏,但却实在力不从心。正如您所看到的,这其实根本就不是我们的专栏;而是属于您们的专栏。您们将精心编写的大量电子邮件发送至scripter@microsoft.com,而这个专栏则恰恰(并将继续)建立在您们所提供的问题和评论基础之上。正因如此,脚本专家组才得以有条不紊地欢迎大家访问属于自己的专栏;我们衷心希望您们能够在此发现既赏心悦目又具备实用价值的信息资料。如果本专栏所呈献的内容并不令您满意,则请不吝赐教!

我们所收到的较为常见一个问题就是,如何从WSH脚本中运行程序。以下是摘自此类电子邮件的一个具有代表性的片断。

你们曾在Web广播中提到过两行可帮助你们运行某一外部程序的脚本代码。请问,你们能否提供有关代码的示例样本?

作为针对上述问题的答复,我们决定占用本专栏第一期的篇幅向您介绍从自行编制的脚本中运行外部程序的具体实现方式。若问您为何需要从脚本中运行外部程序?就请这样设想:命令行工具将允许您执行一整套特定任务,而批处理脚本则提供了所需逻辑线程,以便您用来将这些任务缝合成足以构建自定义解决方案的基本要素。但是,如果批处理脚本所提供的逻辑线程尚不具备足够强大的功能,亦或构建解决方案所需必要组件不能由命令行工具提供,又将如之奈何?而这恰恰是WSH脚本大显身手的用武之地。这种脚本将允许您针对由脚本编程语言(如VBScript)所提供的强大逻辑线程加以应用,并借助强大的工具手段(如WMI和ADSI)以及命令行工具和批处理脚本将您自己的任务解决方案付诸实施。

让我们立即着手编写一个示例脚本。事实上,我们应该在编写脚本之前,先确保具备一个用来保存这个脚本的物理位置。如果在您所使用的计算机上并不存在一个名为C:\Scripts的文件夹,就请返回根目录,并新建这个文件夹。当您完成上述工作后,应打开记事本或其它文本编辑器,并键入以下脚本代码,然后,使用RunIPConfig.vbs文件名将其保存至您的C:\Scripts文件夹。

Set objShell = CreateObject(“Wscript.Shell”)
objShell.Run “ipconfig”

在尝试运行刚刚生成的脚本之前,让我们先对其进行检查,并设法事先指出可能产生的运行结果。在这个脚本中存在着两个有助于我们预测运行结果的暗示。第一行代码中包括动词Create(生成,实际上是CreateObject),而第二行代码则包含动词Run(运行)。可见,这个脚本将有可能先生成某种被称作对象的东西,并随后运行某一特定程序。事实上,情况看上去更象是该脚本将运行一个文件名为ipconfig.exe的命令行工具。

以上预测的确与该脚本所产生的运行结果相符。该脚本首先生成一种被称作对象的东西。在这个示例中,脚本所生成的对象是Shell(WshShell)对象。您可将某一对象理解成类似于命令行工具的东西;而您的脚本则可借助它执行某一特定类型的任务。Shell对象将允许您从脚本中执行原本可在Windows命令解释程序内执行的任务--就像运行程序一样。

与命令行工具不同的是,您必须在开始对其加以应用之前,先行生成一个新对象。当生成该对象时,还应为它指定一个将在脚本其余部分中被用来对它进行引用的名称。在这个示例中,我们所使用的名称是objShell。当对象生成完毕并被赋予相关名称后,我们便可利用该对象执行相关任务,具体方法为:在对象名后加上一个圆点(.),并在圆点后加上所需完成的任务名称。这个例子中的任务名是Run。而Run则是众所周知的Shell对象方法。

现在,请打开一个命令行窗体,并导航至C:\Scripts文件夹。如需运行您刚刚编写的脚本,则请输入cscript RunIPConfig.vbs命令,并按回车键。您将看到一个窗体在屏幕上一闪而过,但该窗体所显示的内容却在您能够看清之前消失了!您可反复尝试运行该脚本,并试图破解消失在窗体中的信息。这也许是一件饶有兴味的事,但却可通过一种更加简便的方法完成。

为了便于您进行理解,我们必须针对在您运行命令行工具的同时所发生的事件给予关注。当您在命令行方式下输入ipconfig、并在键盘上敲击回车键时,便对命令解释程序发出了一个指令。而命令解释程序则将对您所输入的指令进行检查。如果您所输入的命令代表着一个有效程序,那么,命令解释程序便会为您运行该程序。如果您所输入的命令并不代表有效程序,那么,命令解释程序则将通过显示错误信息的方式就有关情况向您进行告知。基于Windows NT操作系统的命令解释程序是Cmd.exe。而Windows 9x操作系统中的命令解释程序则是Command.exe。

此时此刻,上述脚本中的objShell.Run “ipconfig”命令将在其运行ipconfig程序的同时绕过命令解释程序;换句话说,该命令将在无须先运行命令解释程序再由命令解释程序运行ipconfig的前提下直接运行ipconfig。在某些情况下,这种方式或许利大于弊,但在这个示例中,我们却不希望绕过命令解释程序。为什么?因为命令解释程序可帮助我们发现“窗体一闪而过”问题的答案。正如您所看到的,命令解释程序可接受大量开关选项(如果您有兴趣了解cmd.exe或command.exe所具备的全部功能,则请在计算机帮助系统中查找该程序的可执行文件),而这些选项中的/k开关则恰恰用来指示命令解释程序在某一程序运行完毕后保持输出窗口的开启状态。我们需要借助这个选项来确保程序窗体不会在打开后立即关闭。

为实现上述目的,我们应将正在运行的命令从ipconfig修改为cmd.exe /k ipconfig或command.exe /k ipconfig,这里,cmd.exe对应于Windows NT操作系统,而command.exe则适用于Windows 9x操作系统。但是,如果您必须同时面向Windows NT和Windows 9x操作系统提供支持,又将如之奈何?针对我们正在使用的操作系统进行跟踪的确是一件颇为麻烦的事。也正是出于这种原因,我们才引用了一个名为%COMSPEC%的环境变量,以期提供通往适当命令解释程序的完整路径。该环境变量可确保下列脚本代码既可在Windows NT操作系统上运行,又能在Windows 9x操作系统上执行:

Set objShell = CreateObject("WScript.Shell")
objShell.Run "%COMSPEC% /k ipconfig"

请尝试运行这个经过修改的脚本。您只需启动记事本(或其它习惯使用的文件编辑器),并将%COMSPEC% /k添加到先前编写的脚本中,然后,将修改过的文件另存为RunIPConfig.vbs。接下来,在命令行窗体中导航至C:\Scripts目录,再通过输入cscript RunIPConfig.vbs命令并按回车键的方法运行该脚本。您这次将看到一个用来显示ipconfig命令输出结果的新窗体,具体情形如下图所示。


如果您所使用的浏览器无法支持嵌入式框架,请, 点击此处 以便在单独页面中进行浏览。

您接下来应该做的就是进行适用性测验。上述脚本所能运行的程序绝不仅限于ipconfig。而您所需要完成的全部工作仅仅是修改第二行代码中跟在Run方法后面的字符串。您可尝试将%COMSPEC% /k ipconfig分别替换为%COMSPEC% /k netstat或%COMSPEC% /k mmc。举例来说,您将可从某一脚本中运行GUI(图形用户界面)工具,而这个脚本则以运行计算器程序为例:

Set objShell = CreateObject("WScript.Shell")
objShell.Run "Calc.exe"

您不仅能从某一脚本内部运行其它脚本,而且,还可从当前脚本中调用其它批处理文件;如果您碰巧拥有一些批处理文件,不妨尝试运行它们。当然,如果这些批处理文件的存储位置与您计算机上PATH环境变量的设定范围并不相符,您就应将相关存储位置添加到全局访问路径或单独指定通往批处理脚本的完整访问路径。举例来说,如果您在C:\Batchscripts目录下拥有一个名为Checkserver.bat的批处理脚本,那么,应由您传递给Run方法的字符串就应类似于:%COMSPEC% /k C:\batchscripts\checkserver.bat。

如果您已完成了上述试验,我们就将继续讲解后面的内容。您将学习到借助Run方法运行单一程序的具体方法。如果您需要连续运行两个程序,应如何是好?这恰恰是由下列脚本即将执行的任务:该脚本将在运行ping程序后,紧接着运行nslookup程序。请注意,您可将对应于所需运行程序的命令行参数包含在传递给Run方法的字符串当中。在此,我们将把127.0.0.1作为参数分别传递给Ping和Nslookup。

Set objShell = CreateObject("WScript.Shell")
objShell.Run "%COMSPEC% /k ping 127.0.0.1"
objShell.Run "%COMSPEC% /k nslookup 127.0.0.1"

请按针对先前所述脚本的处理方法保存并运行这个脚本。脚本运行结果应该是两个命令行窗体同时打开,其中,Nslookup程序在一个窗体中运行,而Ping程序则在另一个窗体中以同步方式运行,具体情形正如以下屏幕快照所示。


如果您所使用的浏览器无法支持嵌入式框架,请, 点击此处 以便在单独页面中进行浏览。

在这个例子中,事实表明,实现Ping和Nslookup的同时运行根本不成问题。然而,如果您需要运行两个批处理脚本,并希望在第一个脚本运行完毕后再开始运行第二个脚本,又该如何是好?举例来说,第一个程序将针对某一特定类型的信息执行检索,并将其保存为一个文本文件;而第二个程序则将在前者基础上通读刚刚生成的文本文件,并针对相关内容执行特定操作。在这种情况下,两个程序的同时运行将是无法接受时;只有在第一个程序执行完毕的前提下,第二个程序才能开始运行。

到目前为止,我们一直都在传递给Run方法一个用来指定所需运行程序的字符串。Run方法将可再接收两个参数,而这两个参数中的每一个均为可选参数。第二个参数将允许您针对某一程序在开始运行时所呈现的窗体外观加以指定。并非所有程序都将使用到该参数,但对于那些使用该参数的程序来说,该参数却可帮助您启动一个呈现多种窗体风格(包括最小化、最大化和隐藏在内)的程序。我们并不打算在这篇专栏文章中过多涉及该参数。如果您需要了解有关该参数的更多信息资料,敬请查阅WSH在线文档http://msdn.microsoft.com/library/default.asp?url=/nhp/Default.asp?contentid=28001169(点击接近本页面顶部的“Windows Script Host文档”链接)。

专供Run方法接收的第三个参数将允许我们防止相关程序以并发方式运行。您可将第三个参数赋值为True,以便指示脚本停留在使用Run方法的代码行并保持等待状态,直到已被激活的程序运行完毕。我们已经知道了因未设定该参数所产生的后果,该参数在缺省状态下被设定为False,而这种设置必将导致两个程序同时运行。

让我们将前面的脚本修改为先运行Ping程序,待Ping程序运行完毕后,再运行Nslookup程序的状态。

Set objShell = CreateObject("WScript.Shell")
objShell.Run "%COMSPEC% /k ping 127.0.0.1",,True
objShell.Run "%COMSPEC% /k nslookup 127.0.0.1"

请注意,我们已将被赋值为True的第三个参数添加到应在脚本第二行中被调用的Run方法之后。我们之所以连续使用两个逗号的原因在于,如果我们只使用一个逗号,那么,Run方法便会认为我们随后指定的是第二个参数,而非第三个参数。如您所见,两个连续逗号之间并不存在任何内容,因此,也就不会有任何设置被当作第二个参数传递给Run方法。如果我们没有对某一参数专门赋值,那么,Run方法便会使用缺省值。举例来说,第三个参数的缺省值应该是False。

现在就让我们试着运行经过修改的脚本。您会马上注意到,只有Ping程序得到运行。当Ping运行完毕后,该程序运行时所使用的窗体仍在屏幕上保持开启状态;而Nslookup程序则尚未开始运行。请试着关闭这个窗体并查看接下来发生的情况。当您关闭Ping程序所使用的窗体后,Nslookup便会开始运行。请记住,您正在使用/k选项运行cmd.exe(或command.exe),也就是说,您已经告诉Run方法,直到cmd.exe运行完毕,方可继续执行后续指令。好的,由于我们事先设定了/k选项,因此,只有曾被要求保持开启状态的窗体得到关闭,cmd.exe才会执行完毕。

如果您需要运行两个批处理文件,并且无需查看它们的输出结果,那么,便可将/k选项删除,而这两个批处理文件仍将接续(而非同时)运行,这也恰恰是您在第二个程序信赖第一个程序运行结果的前提下所需确保的运行方式。

两个程序之间应就以下信息进行沟通,那就是,第一个程序实现运行的情况必须被告知第二个程序。某些程序将返回一个错误代码(也就是一个数字),用来指示自身是否已得到成功运行。而Run方法便可将这种错误代码返回给您所编写的脚本。正处于运行状态的程序必须在得知即将被返回的错误代码之前运行完毕,为此,您必须通过将Run方法的第三个参数设定为True的方式强制脚本等候程序运行完毕。

我们将在接下来的脚本中运行Ping程序,并通过针对Wscript对象使用Echo命令的方式将该程序所返回的错误代码显示出来。Wscript对象将不必被生成为类似于WshShell对象的状况,而您则可在脚本当中的任意控制点对它所提供的方法加以运用。

Set objShell = CreateObject(“Wscript.Shell”)
iErrorCode = objShell.Run(“ping 127.0.0.1”,,True)
Wscript.Echo iErrorCode

除具备错误代码检索功能外,以上脚本还在两方面与先前脚本有所区别。首先,请注意,即将被传递给Run方法的字符串已被加上了一对圆括号。这主要是因为,Run目前被要求返回一个数值。这属于VBScript语言的一项要求;如果您并未在此使用圆括号,便会在脚本运行过程中收到一条错误信息。应该被返回的数值是Ping程序的错误代码,并将被保存至变量iErrorCode,然后,借助WScript.Echo命令被显示出来。

需要给予关注的第二件事就是,我们并没有运行命令解释程序。我们希望错误代码来自Ping,而非来自Cmd.exe(或Command.exe),为此,我们直接运行了Ping程序。而由Ping返回的错误代码既不是很有趣,也不是很有用。当然,某些命令行工具却可返回可供您所编写的脚本做出相关判断(或生成日志)的有用错误代码。

从借助Run实现运行的某一程序中所获得的相关信息仅能满足最低使用需求,并很有可能不具备多少使用价值。举例来说,许多程序都将只返回一个0数值,以此表明自身已实现运行并且运行完毕。当然,它们并没有必要就自身成功运行且运行完毕的情况向您进行汇报。

当然,如果您已拥有WSH 5.6版(可从http://msdn.microsoft.com/library/default.asp?url=/nhp/Default.asp?contentid=28001169页面下载),便具备了另一种可供使用的方法,以便运行那些可帮助您与当前所运行程序改善信息交流方式的程序。而这种方法便是Exec。它还是从属于WshShell对象的一种方法,这里所说的WshShell正是为使用Run而必须生成的那个对象。这对我们来说主要意味着,不必为开始利用Exec方法而针对脚本的第一行代码进行修改。

让我们直接编写一个利用Exec方法运行ipconfig程序的脚本。

Set objShell = CreateObject("WScript.Shell")
objShell.Exec "%COMSPEC% /k ipconfig"

上述脚本同我们为您展示的第二个脚本之间所存在的唯一区别就是,我们已在这个脚本中将objShell.Run修改为objShell.Exec。然而,当您运行这一个脚本时,将难以觉察到上述修改所产生的效果。您甚至无法看到那个一闪即逝的窗体。实际上,您根本看不到任何东西。如果我们此时告诉您,ipconfig实际上已运行完毕,不知您是否愿意相信?好吧,还是不要轻易相信我们所说的话;取而代之的做法是,在ipconfig命令后方添加一个重定向符号(>),并在针对ipconfig发出的调用后紧跟一个文本文件名。上述代码将导致命令执行结果被输出到指定文件。如果您尚未习惯使用重定向操作,不妨这样设想:如果我们将真实情况告诉您,而ipconfig程序的确处于运行状态,那么,它便会生成自己的标准文本显示信息。遗憾的是,我们将无法看到相关信息。重定向操作将允许我们捕捉到任何命令的输出结果,并将它们强行导向一个指定文件。这将有助于我们针对ipconfig命令是否处于运行状态加以确认。接下来的脚本可供用来将所需查看的输出结果呈现给我们。

Set objShell = CreateObject(“Wscript.Shell”)
objShell.Exec “%COMSPEC% /k ipconfig > ipconfig_output.txt”

在上述脚本运行完毕后,请查看ipconfig_output.txt文件内容。如需查看这个文本文件的内容,则请按下列屏幕快照所示键入type命令。


如果您所使用的浏览器无法支持嵌入式框架,请, 点击此处 以便在单独页面中进行浏览。

这就是您所得到的信息!我们的脚本就是实实在在地运行ipconfig,只不过没有让我们看到命令输出结果。无论您相信与否,这都算得上一件好事。Exec之所以没有为我们显示输出结果,恰恰是因为我们原本并未提出相关要求。正如您所看到的那样,我们可借助Exec针对输出结果的呈现和操作方式加以控制。

您是否还记得令Run方法返回错误代码的具体实现方式?Exec事实上可返回一个被称作WshScriptExec的完整对象。正如WshShell对象为我们提供针对Run和Exec这两种方法的访问调用权限那样,WshScriptExec对象将为我们提供针对附加功能特性的访问调用权限,而这些附加功能特性则允许我们围绕某一程序的输出结果展开操作。脚本中大量操作的实现都会以额外复杂性为代价,而上述附加功能特性也不例外。我们即将或多或少地遭遇这种复杂性,为此,请继续阅读本文,并在接下来的章节中确保集中精力。

Exec方法可供用来返回一个WshScriptExec对象。而在某一脚本中促成这一事件的发生则需要使用VBScript语言中的Set关键字,具体代码如下所示:

Set objWshShellExec = objShell.Exec(“ipconfig”)

说明:我们并未在此继续使用%COMSPEC%环境变量;该变量已不具备继续存在的必要,其原因主要体现为,我们不需要为确保某一并不存在的窗体处于开启状态而将/k选项传递给相关命令。那么,我们又该如何将这个窗体关闭并继续运行脚本代码呢?我们将可在此使用/c选项(该选项表示只运行一条命令,然后,接着执行后续代码);事实上,如果我们需要运行一条已被内建于命令解释程序的命令(例如dir),便会需要使用该选项。而像dir这样的工具本身并不属于工具范畴;它们更加近似于cmd.exe(或command.exe)命令的参数;正因如此,您才需要为确保它们发挥作用而指定%COMSPEC%这一环境变量。

当您将与WshShellExec对象相关的引用存储于objWshShellExec变量后,便已具备了开始围绕程序输出结果展开操作的必要条件,但此时的条件尚不够充分。而这正是复杂性乘虚而入的大好时机。正如Exec方法返回WshShellExec对象那样,WshShellExec对象亦可返回另一对象;这个对象将通过调用StdOut属性的方式被返回,而这恰恰允许您针对程序输出结果执行相关操作。

如果不对照一个示例脚本,上述思路几乎不可能得到理解。为此,我们专门编写了一个最为简单的示例,旨在针对由嵌套于WSH脚本的某一程序所产生的输出结果加以运用。请将该脚本当作一种特殊类型的重定向操作。只有在这种情况下,程序输出结果才不会被导入某一文件,取而代之的是,相关输出结果将被赋予一个名为strOutput的变量。遗憾的是,我们这回将不能仅仅使用一个简单的重定向符号(>),而必须围绕两个不同对象执行操作。

Set objShell = CreateObject("WScript.Shell")
Set objWshScriptExec = objShell.Exec("ipconfig")
Set objStdOut = objWshScriptExec.StdOut
strOutput = objStdOut.ReadAll
WScript.Echo strOutput

由上述脚本生成的输出结果将被显示在专供脚本运行的窗体内;这里并没有新的窗体生成。事实上,如果您从命令行方式中运行ipconfig本身的话,输出结果看上去也将别无二帜。既然如此,为何不干脆采取这种方式?我们已经在脚本当中为输出结果生成了一个中间存储位置--strOutput变量。尽管我们将在上述脚本的下一行内利用Wscript.Echo命令针对输出结果加以显示,然而,却往往需要在执行显示操作前先对输出结果实施操控,只有这样,才能对由ipconfig工具显示的信息进行修改。

在尝试运行上述脚本之前,让我们先对它的最后三行代码给予更多关注;当然,您已经对前两行代码非常熟悉了。在第三行代码中,WshScriptExec对象所配备的StdOut属性将被用来检索代表正处于运行状态之程序(标准)输出结果的相关对象;而ipconfig则是本例中待处理结果的输出程序。在第四行代码中,ReadAll方法将被用来针对全部输出结果执行检索,并将其保存于strOutput变量。最后,也就是在第五行代码中,WScript.Echo将被用来显示输出结果。

我们现在已拥有了经常被当作ipconfig工具(或可供我们用来在上述脚本中替代ipconfig的其它任何工具)封装程序的对象。封装程序是一种可就另一程序功能特性加以应用、并能够以简便快捷方式针对已被封装程序功能进行修改的特殊程序。封装程序为我们创造了针对已被封装程序的输出结果进行修改的理想时机。而这恰恰是我们将利用本文剩余篇幅加以介绍的内容。

在围绕ipconfig工具开展自定义的过程中,我们将需要了解以每次一行的频率对程序输出结果进行相关处理的具体方法。当前,我们主要借助objStdOut.ReadAll对ipconfig工具的全部输出结果执行检索操作,并将检索结果保存于strOutput变量。而我们将在下一个脚本中执行的任务则是利用ReadLine(而非ReadAll)针对输出结果进行逐行检索。我们将对输出结果的每一行进行检查,如果任意一行包括“Physical Address”(物理地址)字符串,就会将它显示出来;否则,便不会显示这行输出结果。我们只对包含“Physical Address”(物理地址,也就是对应于网卡的MAC地址)字符串的输出结果行执行了筛选操作,并未将其它任何内容显示出来。

Set objShell = CreateObject("WScript.Shell")
Set objWshScriptExec = objShell.Exec("ipconfig /all")
Set objStdOut = objWshScriptExec.StdOut

While Not objStdOut.AtEndOfStream
   strLine = objStdOut.ReadLine
   If InStr(strLine,"Physical Address") Then
       WScript.Echo strLine
   End If
Wend

上述脚本的前三行看上去多少有些眼熟;请注意,我们必须将/all选项添加至ipconfig命令后方,以确保Physical Address(物理地址)被显示出来。这个脚本同上个脚本之间的主要区别在于,前者使用了While/Wend循环语句。我们将使用ReadLine方法对单个输出结果行执行检索操作,但却需要掌握针对是否超出行检索范围的状态加以判定的具体手段。而这恰恰是While/Wend循环和objStdOut.AtEndOfStream指令的用武之地。只要程序尚未触及(标准)输出结果数据流的结尾,上述循环便会保持接续执行状态(而符合检索条件的输出结果行则将被保存至strLine变量)。您可将一个数据流想象为由一系列字符排列而成的长串,而您则可按排列顺序对其进行相关处理;本例中的数据流便是ipconfig /all命令的完整输出结果。

每当我们完成一次程序循环时,便会将下一行内容保存至strLine变量,并利用If/Then语句决定是否应对该行内容加以显示。我们主要借助InStr(字符串命令)功能对存储在strLine变量当中的输出结果行是否包含“Physical Address”(物理地址)加以判断。如果当前行确实包含有“Physical Address”(物理地址),就应使用WScript.Echo命令将它显示出来。以下屏幕快照展现了由脚本生成的部分典型输出结果。


如果您所使用的浏览器无法支持嵌入式框架,请, 点击此处 以便在单独页面中进行浏览。

我们的下一个脚本将使用与上一个脚本完全相同的基本组件,所不同的是,我们已不再针对输出结果执行筛选操作,而是向它们当中添加特定内容。鉴于ipconfig /all命令可针对DHCP释放获取时机及其超时状态信息执行检索,因此,将当前日期和时间包含于命令输出结果的处理方法或许具备一定实用价值。接下来的脚本正是用来实现这一设想。

脚本一开始便针对输出结果的前四行执行检索和显示操作,并且对每一行所采取的方法完全相同--先调用ReadLine命令,紧接着调用WScript.Echo命令。当输出结果的第四行已被检索并显示出来后,我们便生成了一个包含有当前日期和时间标注信息的字符串。而这里所说的当前日期和时间则是由VBScript所配备的Now()函数提供的。

Set objShell = CreateObject("WScript.Shell")
Set objWshScriptExec = objShell.Exec("ipconfig /all")
Set objStdOut = objWshScriptExec.StdOut

'
' Skip first four lines
'
strLine = objStdOut.ReadLine
WScript.Echo strLine
strLine = objStdOut.ReadLine
WScript.Echo strLine
strLine = objStdOut.ReadLine
WScript.Echo strLine
strLine = objStdOut.ReadLine
WScript.Echo strLine

'
' Add date/time information
'

strCurrentTime = "   Current Date/Time. . . . . . . . .: " & Now()
WScript.Echo strCurrentTime

'
' Display the rest of the output
'

While Not objStdOut.AtEndOfStream
   strLine = objStdOut.ReadLine
   WScript.Echo strLine
Wend

毋庸置疑,上述脚本已开始变得多少有些复杂了。而解读这一脚本的最佳方法则是逐段分析。您应对该脚本的前三行较为熟悉。而下一个附有注释的部分则主要借助ReadLine和Echo命令对ipconfig /all输出结果的前四行执行读取和显示操作。

附有注释的下一部分自然以编排将被临时存储于strCurrentTime变量的新信息行为起点。这个信息行将由一个与VBScript Now()函数调用操作相关联的简单字符串构成,而VBScript Now()函数则用来提供当前日期和时间信息。一旦这个信息行编排完毕并被保存于strCurrentTime变量,我们便可当即使用WScript.Echo命令对其加以显示。

在最后一个附有注释的部分中,我们将对输出结果的剩余内容执行循环操作,并在无需进行任何修改的前提下对其进行逐行显示。以下屏幕快照展现了脚本运行产生的典型输出结果。请注意,脚本输出结果看上去与ipconfig的标准输出结果别无二帜。而由我们插入Host Name(主机名称)行下方的Current Date/Time(当前日期/时间)行则与全新的上下文环境结合得天衣无缝。


如果您所使用的浏览器无法支持嵌入式框架,请, 点击此处 以便在单独页面中进行浏览。

最后,让我们使用For/Next循环语句执行前四行内容的输出操作,以便使脚本变得更加精简,具体代码如下所示。

Set objShell = CreateObject("WScript.Shell")
Set objWshScriptExec = objShell.Exec("ipconfig /all")
Set objStdOut = objWshScriptExec.StdOut

'
' Skip first four lines
'
For i = 1 To 4
   strLine = objStdOut.ReadLine
   WScript.Echo strLine
Next

'
' Add date/time information
'

strCurrentTime = "   Current Date/Time. . . . . . . . .: " & Now()
WScript.Echo strCurrentTime

'
' Display the rest of the output
'

While Not objStdOut.AtEndOfStream
   strLine = objStdOut.ReadLine
   WScript.Echo strLine
Wend

事实上,还存在着从WSH脚本中运行其它程序的另一种替代方法:即针对Windows管理规范(WMI)加以应用。您将可借助WMI完成一系列Windows管理任务!虽然借助WMI实现程序运行的方式将涉及相对复杂的语法,但却可帮助您完成Run和Exec无法实现的任务,例如,设定程序运行优先级或基于远程计算机运行相关程序。在此,我们诚恳地建议您尽可能了解有关WMI的更多信息资料。如果您确实对此抱有兴趣(我们希望您能够这样),则请到MSDN网站上一个名为Scripting Clinic(脚本诊所)的专栏中查阅由三部分组成的系列文章(http://msdn.microsoft.com/columns)。

好在我们已回答了您针对从WSH脚本中运行其它程序而产生的某些疑难问题。我们还为您提供了有关Run和Exec方法的简要概述,并初步涉及了存在于两者之间的某些差异--其中,最明显的区别体现为,Exec方法将允许您针对程序输出结果进行处理,而Run方法则不具备这种能力。另一个重要区别是:只有WSH 5.6及其后续版本方可提供Exec方法。

我们衷心希望您能够对第一篇专栏文章感到满意。请将您的意见和观点发送至:[email=scripter@microsoft]scripter@microsoft[/email],以便让我们有所了解。此外,请务必告诉我们您希望以后各期专栏能够涉及哪些内容。如果您能够在百忙之中抽出宝贵时间,则请查阅刊载于Scripting Clinic(脚本诊所)专栏的WMI系列文章;如果您利用在这里学到的知识技能完成了某些既简洁明快又富于实用价值的工作,就请将事情的经过记录下来;我们非常乐于听到与这方面有关的消息!

作者: electronixtar     时间: 2006-6-9 10:55
呵呵,对!

最近研究XMLHttp,做网站自动登陆,必须输出一个 .js,里面有个函数,我就用 WshShell.Exec "cscript xxxx.js //NoLogo"来获得函数值的,呵呵~~很有用的方法~~
作者: vlq5299     时间: 2006-6-11 17:51
是的
作者: mobo     时间: 2006-9-19 08:29    标题: SYSTEMINFO在CMD下不便输出文本文件,用标准输出就行了!

'''SYSTEMINFO在CMD下不便输出文本文件,用标准输出就行了!
'''自带参数的用法 ---------By MOBO[墨伯]:  

Set objShell = CreateObject("WScript.Shell")
Set objExecObject = objShell.Exec("%comspec% /c SYSTEMINFO  /FO LIST" )

'''读取运行后的标准输出:
Do While Not objExecObject.StdOut.AtEndOfStream
   strText = objExecObject.StdOut.ReadAll()
Loop

'''用文本文件输出:
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.CreateTextFile("SYSTEMINFO.txt", true)
      ts.Write strText
Set ts=nothing
Set fso=nothing
'''本来想用网页输出的,但不美!
作者: electronixtar     时间: 2006-9-19 08:58
有vbs+WMI就不用systeminfo费事了
作者: lxmxn     时间: 2006-10-8 21:14

我按照楼主说的做了,可以结果并没有显示出来。

就是一个msgbox消息框跳出来了,里面什么消息都没有,这是咋回事哦?

作者: electronixtar     时间: 2006-10-8 21:46
不会的吧,注意要有参数哦
作者: lxmxn     时间: 2006-10-8 22:21


  Quote:
Originally posted by mobo at 2006-9-19 08:29:
'''SYSTEMINFO在CMD下不便输出文本文件,用标准输出就行了


可以输出到文本文件啊,为什么说不便?

作者: lxmxn     时间: 2006-12-28 12:11

  再来试了一下,果然可以,看来当初是我搞错了,没有加参数。

  另外,mobo的vbs脚本,和 systeminfo > systeminfo.txt 的效果是一样的。测试通过。

作者: jmz573515     时间: 2006-12-28 21:29
不错,顶上去!
作者: gne3     时间: 2007-2-18 04:43
ding