原创 (转载)脚本的故事--虫子爬进来了,但却赖着不走收藏

脚本的故事

有关所有“脚本的故事”专栏的列表和其他信息,请单击此处

转自:http://www.microsoft.com/china/technet/community/columns/scripts/sg0904.mspx
本页内容

*

虫子爬进来了,但却赖着不走

我们都曾听说过海豚如何如何的聪明,还有大猩猩竟然能够使用手语进行交流!我们甚至还阅读过有关新喀里多尼亚岛的小鸟能够使用工具的文章。(实话实说,这些小鸟只是用嘴衔着一根树枝,并试图将树缝中的虫子挖出来;尽管如此,这已经够神奇的了,您总不能让它们使用台锯或电钻之类的东西吧。)

这已经够好的了,不是吗?我们很高兴地看到动物王国里竟然有这么聪明的家伙;好好干,希望它们有一天会干出点更出彩的事情来!打手语或使用工具并没有什么不好。不过,至少 一个方面其他动物还无法与我们人类分庭抗礼,那就是我们可以将整个物种统统消灭。

的确如此。候鸽要与人斗,结果会怎样?那还用说嘛,举双爪投降。渡渡鸟要与人斗,结果会怎样?小鸟定会落荒而逃!新喀里多尼亚岛的乌鸦就算能够使用工具挖虫子,那又有什么了不起的;说到底,是谁把非洲蓝羚羊赶尽杀绝的?给您提个醒:这事的确不是新喀里多尼亚岛乌鸦干的。

澄清一下。 当然啦,脚本专家都是喜欢开玩笑的,我们可不喜欢搞什么种族灭绝之类的事;事实上,我们不希望任何动物受到伤害。

当然,邻居家整天吠个不停的恶狗就另当别论了。还有那只猫整天在街上鬼鬼祟祟的,头上还像模像样地戴着一条蓝头巾。咳,它们就这德行。

当然,在搞种族灭绝方面,至少有一种“动物”一点也不比我们人类差,尽管我们曾经大力围剿,它们却一直负隅顽抗,您一定很奇怪,那会是谁呢?这就是电脑虫。首次报道电脑虫是在 1947 年,但是直到现在,我们也没有将其完全消灭;事实上,恰恰相反,这些害虫一天比一天多。而且,这些害虫产生的危害也相应地变得越来越严重;据估计,单单这臭名昭著的千年虫就给全世界造成了超过 3000 亿美元的损失。

历史典故。 据传,第一个电脑虫确实是一只虫子。在 1947 年,世界上最早的一批计算机之一 Mark II Aiken 继电器式计算器出现了故障。程序员经过调查发现,有一个蛾子夹在继电器中间,因而造成机器短路(它参与了“计算”)。人们随后将蛾子取了出来,并制成标本夹在 Mark II 运行日志中,还在日志中注明这是“人类发现的第一例电脑虫”。信不信由您,斯密森南研究院至今还珍藏着这只小虫子。(显然,由于米开朗基罗和达芬奇去世这么多年了,博物馆实在没有什么东西可供展览的了。)

我们知道电脑虫(也许更确切的说法是,使脚本无法正常运行的代码错误)是系统管理员非常关注的问题。总之,在最近的“脚本周”网络广播系列期间,我们收到了很多类似下面的问题:

“我可以使用什么工具来调试代码吗?”

“您知道哪种软件可以帮助我调试代码并修复脚本问题吗?”

“我可以从什么地方下载脚本调试器?”

事实上,一个地方可以下载脚本调试器,那就是 Microsoft.com。Microsoft 提供了(免费的)脚本调试器,但好像没有多少人知道;这种调试器界面有一点怪,但它确实提供了很多在功能完备的开发环境(如 Visual Studio)中才有的功能。如果您正在寻找可帮助您调试脚本的工具,从 Microsoft Script Debugger 入手是一个不错的选择。

注意。 如果 Microsoft 有免费的脚本调试器,那么我们为什么不花大力气进行宣传呢?这可问住了我们。这很有可能是因为 Microsoft 代码无一例外地都没有错误,因此,我们从未想过调试器之类的东西。

请注意,我们说的是很有可能……

顺便说一句,如果您购买了 Windows 2000,则您已经 Script Debugger 了;它作为安装选件包含在 Windows CD 中。Windows XP 或 Windows Server 2003 不提供该调试器,但您可以下载适用于这两种操作系统的版本以及适用于 Windows 98、Windows ME 和 Windows NT 4.0 的版本。

如果您正在查找下载位置,请试试下面的链接:

适用于 Windows NT 4.0、2000 和 XP 的 Microsoft Windows Script Debugger
(http://www.microsoft.com/downloads/details.aspx?FamilyID=2f465be0-94fd-4569-b3c4-dffdf19ccd99&DisplayLang=en)。

适用于 Windows 98 和 Windows Me 的 Microsoft Windows Script Debugger
(http://www.microsoft.com/downloads/details.aspx?displaylang=zh-cn&FamilyID=e606e71f-ba7f-471e-a57d-f2216d81ec3d)。

启动 Script Debugger

Microsoft Script Debugger 是一个相当好的小工具,但正如我们所注意到的一样,它的样子有点怪怪的。我们先说说您要注意什么问题。在下载并安装 Script Debugger 后,您第一个想法可能就是启动调试器并在其中装载一个脚本。千万不要这样做,这不管用。的确,调试器会启动并且您可以 装载脚本,但此时会出现一点问题;注意,所有调试命令都是灰显的,您实际上不能执行任何操作:

Script Debugger

查看大图。

不过说真的,要是其他 Microsoft 软件连最基本的功能都没有的话,我就不用这么累写稿子了!不要担心:Script Debugger 实际上是好用的,只是当您手动启动它并装载脚本时,它才会出现无法工作的情况。您需要从命令行启动脚本并传递参数 //x。例如,要在 Script Debugger 中装载脚本 my_script.vbs,您应该键入类似下面的命令:

cscript my_script.vbs //x

注意。 Script Debugger 为什么会是这样呢?我们也不知道。您可以试着问一下海豚,据说它们是非常聪明的……

实际上,这个问题的答案很可能与以下事实有关:Script Debugger 最初是为调试 ASP 和 HTML 页设计的。还有一则好消息是,此调试器还适用于独立的 VBScript 和 Jscript 文件。

不可否认,这听起来有点荒唐,但这种方法的确奏效。使用 //x 参数启动脚本后,您会发现所有调试命令现在都可以使用了:

werw

查看大图。

重要说明。 在继续讨论之前,我们应该指出 Script Debugger 工具允许您在很大程度上控制如何运行脚本;但是,它并不是那种十全十美的环境,在运行脚本时可能会对其他程序造成影响。在使用 Script Debugger 时,脚本实际上正在运行;事实上,如果您要在工作时查看脚本输出情况,您可以使用 Alt-Tab 组合键在调试器和命令窗口之间来回切换。如果所调试的脚本可删除 Active Directory 中的所有用户帐户,您可不要将调试会话当作是彩排或演习;在会话结束时,它将会删除 Active Directory 中的所有用户帐户。该调试器是一个故障排除工具,但它并不是沙箱或某种虚拟环境。

那么,在将脚本装载到 Script Debugger 中后,该怎么办呢?虽然有几种可供选择的选项,但我们重点介绍以下任务:

分步执行代码

设置和删除断点

处理变量

运行脚本命令

分步执行代码

举一个软件的例子,Microsoft Word 是一个事件驱动的应用程序。在启动 Word 时,它不执行任何操作;它只是静静地等待事件的发生,等待您单击鼠标按钮或按键盘上的键或者执行某种操作。(当然了,如果等待时间过长,即使在您执行某种操作之后,它也不会做出任何响应,但这已超出了本文讨论的范围。)

与之相比,脚本通常是过程驱动的:在启动后,它们通常并不等待事件发生,而是直接运行。在脚本启动后,它运行第一行代码,然后(甚至没有停下来“喘口气”)开始运行其余的代码行,整个过程一蹴而就。在运行完代码行后,脚本就会自动终止。

只要一切正常,这就是一种很好的模型。然而,如果执行情况与计划的情况不完全一致时,此模型就会中止一段时间。例如,假定有一个用于完成以下任务的脚本:

在本地计算机上创建一个文本文件。

从几个服务器中检索硬件信息。

将检索到的信息写入所创建的文本文件中。

将该文本文件从本地计算机复制到远程计算机上。

从本地计算机上删除该文本文件。

您运行该脚本,一眨眼的功夫,脚本就完成了它的任务。您检查本地计算机,没有文本文件。这很正常呀!毕竟,脚本就是应该从本地计算机上删除文本文件。现在,检查一下远程计算机:也没有文本文件。麻烦了!显然,出现了问题,但到底出现了什么问题,问题出在哪里呢?即使这是一个相对简单的脚本,脚本也可能会在很多地方出现错误。我怎么能知道问题到底出在哪里呢?

注意。 我们假定此脚本中包含防止脚本崩溃的 On Error Resume Next。但要记住,即使删除 On Error Resume Next 也不一定能找出实际发生错误的确切位置。请考虑下面的简单脚本:

intMyNumber = 2
A = intMyNumbr
B = 3
C = B / A

如果您运行此脚本,就会在第 4 行出现错误,这是因为除数为 0。但实际上问题并不是出在第 4 行,问题实际出在第 2 行,您在该行中将变量 A 设置为 0 而不是 2。这是由于拼写错误造成的:您为 A 分配 intNumbr 的值而不是 intNumber 的值。因为 intNumbr 没有 值,所以将 A 赋值为 0,而不是您希望分配的 2。

不可否认,这是一个很容易找出来的错误。但问题的关键是,在脚本崩溃时显示的错误信息只告诉您错误在哪一行中显现了出来;也就是说代码中的错误在哪一行中实际引发了错误。错误的根本原因(如将变量设置为 0)可能在数百行代码之前就已经存在了。

处理此类问题的一种方法是使用 Script Debugger 来“分步执行”代码。分步执行代码即逐行运行脚本。不可否认,如果脚本很长,这可能会是一项冗长而乏味的工作;不过,我们一会将向您介绍一种解决方法。另一方面,通过分步执行代码,您可以在每一步停下来并确保脚本正常工作。

例如,我们假定的脚本在运行时首先应该创建一个文本文件。在我们按原样运行脚本时,我们不知道是否在第一个地方创建了文本文件。但通过分步执行代码,我们就可以非常方便地验证这一点。我们运行将创建文本文件的代码行,然后停止。随后,我们打开 Windows 资源管理器并检查文本文件是否存在。如果文件存在,则继续分步执行脚本的其余部分。如果文件不存在,则我们就已找到了一处错误。

那怎么在 Script Debugger 中分步执行代码呢?实际上这是非常容易的:在调试器中装载脚本,然后按 F8 键。每次按 F8 键时,调试器将执行一行代码,跳到 一行代码,然后等待您再次按 F8 键。(顺便说一句,如果您不喜欢使用键盘,您也可以从 Debug(调试)菜单中选择 Step Into(单步执行))。一直按 F8 键,直至到达脚本的结尾。您也可以从当前行开始运行脚本的其余部分。为此,请按 F5 键或者从 Debug 菜单中选择 Run(运行)。

警告。 假定您调试的脚本从事件日志中检索事件,并且假定这些事件日志中有 5,000 个事件。在分步执行使用 For Each 循环的代码时一定要小心;脚本不只运行该循环一次,它将运行 5,000 次,为集合中的每一项分别运行一次。对于此类情况,您可能只需要运行循环一次或两次(只为确认循环运行是否正常),然后按 F5 键以运行脚本的其余部分。或者,您也可能需要使用断点,我们稍后将对其进行介绍。

最后一步

还有最后一件怪事,我得提醒您一下。您已经运行了一遍脚本并且一切正常。但小心不会出大错,对吧?因此,您希望再运行一次脚本,这样才会安心。没有问题。但您必须退出 Script Debugger 并重新装载脚本,否则就可能会出问题。由于某种原因,您只能在 Script Debugger 中运行脚本一次;在完成后,您需要退出调试器并重新启动。

知道了,知道了。别生气,我们也不认为这是一种特别好的做法;事实上,我们开始怀疑,与其说人类灭绝了非洲蓝羚羊,倒不如说软件的用户界面设计离奇古怪。但至少我们已经向您说明了 Script Debugger 的怪异之处;可怜的非洲蓝羚羊怕是永远也看不到这一天了。

设置和删除断点

假定您的脚本有 1,000 行,并且您系统地分步执行了代码(逐行执行)。一切似乎都很正常,但在第 933 行发现了一处错误。您停止了调试器并更正了脚本错误。现在,您需要重新在调试器中装载脚本并再次运行,但您确实不想再重新执行一遍前 932 行。不管怎么说,您都非常确定这些代码行没有什么错误。但除了重来一遍,您还有什么选择吗?

如果您是新喀里多尼亚岛的乌鸦,那就一点选择都没有了。作为人,您比它们可要聪明多了,您可以走捷径呀,即在第 933 行设置一个断点。您可能会问,什么是断点?从用途上来讲,断点就像插入到代码中的“停车标志”。如果您在 Script Debugger 中运行脚本(通过按 F5 键或从 Debug 菜单中选择 Run),脚本将一直运行至断点处,此时它会“嘎然停止”。您可以从此处开始分步执行代码,也可以选择 Run 来运行脚本的其余部分。

在 Script Debugger 中,您可以非常容易地识别出断点。事实上,它们与下图中的情形非常类似:

识别断点

查看大图。

注意以红色突出显示的内容和类似停车标志的小红图标。

那怎么设置断点呢?只需将光标放置到到目标行的任何地方,然后按 F9 键(或者从 Debug 菜单中选择 Toggle Breakpoint(切换断点))。要删除断点,请再次按 F9 键,或者按 Ctrl-Shift-F9 组合键删除脚本中的所有断点。

您可能已经猜到了,断点是逐行分步执行代码的一个很好的替代方案。您确信脚本的前 932 行都是正确无误的吗?没问题;那只需在第 933 行设置一个断点并运行脚本即可。脚本将快速通过所有这些行,直至到达断点处;然后突然停止。此时,您可以开始分步执行代码的其余部分。

处理变量

正如前面所提到的一样,变量被设置为错误值或意外值这一问题是脚本中经常出现的错误。更糟的是,此类错误可能很难找出来,尤其是在较长的脚本中,其中的变量值可能会改变很多次。那到底怎么才能在运行脚本时跟踪变量的当前值呢?

一种方法是在 Script Debugger 中分步执行脚本,并定期向调试器查询变量的当前值。有没有更简单的方法呢?

让我们使用一个很简单的脚本来试一试。以下脚本(我们保存为 Test.vbs)为变量 A 赋值 2,并为变量 B 赋值 3。然后脚本执行一些计算,并将这些计算的累积结果分配给变量 C。脚本本身类似于以下内容:

A = 2
B = 3
C = A + B
C = C * A
C = C^B
Wscript.Echo C

如果您运行此脚本,返回的答案应该为 1000。好极了,是吧?但是,1000 到底是不是您应该得到的答案呢?谁知道呢?更糟的是,您到底如何才能开始确定这是不是正确答案呢?

您可以做的一件事是在 Script Debugger 中装载脚本,分步执行代码,然后定期向调试器查询以了解各个变量的值。请执行以下操作:在 Script Debugger 中装载脚本,并分步执行前三行代码。突出显示的位置应该是 C = C * A,屏幕显示应类似于以下内容:

text

查看大图。

到现在为止,我们的脚本运行情况如何?我们可以从这里开始进行复核。我们知道 A 等于 2,B 等于 3,我们刚刚执行了等式 C = A + B 的运算。换句话说,C = 2 + 3,这就是说 C 应该等于 5。我们 知道这一点,但我们的脚本 是否知道呢?

好,让我们来问问它。在 Script Debugger 中,从 View(视图)菜单中选择 Command Window(命令窗口)。您现在应该看到一个类似于此屏幕内容的小窗口:

text

我们可以通过命令窗口与脚本进行交互;我们可以向它提问题,正如我们稍后会看到的,我们甚至还可以使用它来向脚本发出命令。我们先使用问号 ? 作为命令,查询一下变量 C 的当前值:

? C

换句话说,在命令窗口中键入 ? C,然后按 Enter 键;您会立即获得变量 C 的当前值。

text

不错吧?现在,按 F8 键执行下一行代码 (C = C * A)。我们知道 C 等于 5,而 A 等于 2;因此,在运行此代码行后,我们预计 C 应该等于 10 (5 * 2)。因此,让我们使用命令窗口再看一下变量 C 的当前 值:

text

好家伙,我们这不得做上一天呀!执行下一行代码 (C = C^B)。在此代码行运行后,C 应该等于 1000 — 10(C 的当前值)的 3 次幂(因为 B 等于 3)也就是 1000。您猜呢?

text

有没有更好的办法呢?

运行脚本命令

那么,与在脚本运行时查询脚本并获取有关脚本所执行操作的信息相比,到底有没有 更好的办法呢?没有,当然没有,除非您能在脚本运行时真的向其发送命令。但这是很荒谬的;在脚本运行时无法向其发送命令。难道真的能行……

让我们看另一个简单的小脚本,它返回有关 Internet Explorer 附加组件的信息:

strComputer = "atl-ws-01"
Set objWMIService = GetObject("winmgmts:\\" & strComputer _
    & "\root\cimv2\Applications\MicrosoftIE")
Set colIESettings = objWMIService.ExecQuery _
    ("Select * from MicrosoftIE_Object")
For Each strIESetting in colIESettings
    Wscript.Echo "Code base: " & strIESetting.CodeBase
    Wscript.Echo "Program file: " & strIESetting.ProgramFile
    Wscript.Echo "Status: " & strIESetting.Status
Next

正如所编写的一样,此脚本连接到远程计算机 (atl-ws-01),然后从 MicrosoftIE_Object 类中检索某些信息。又是老一套,是吧?我们想通过在 Script Debugger 中分步执行来测试此脚本,因此,我们装载此脚本,按 F8 键运行第一行代码,该行将变量 strComputer 的值设置为 atl-ws-01。

但是,此时我们意识到有一个问题:我们突然想起计算机 atl-ws-01 没有联机。因为我们无法连接到该计算机,所以脚本注定会失败。您一定以为我们必须退出 Script Debugger,更改脚本(将其指向另一台计算机),然后重新运行脚本,是吧?

错。脚本有问题吗?问题是出在我们要连接到由变量 strComputer 表示的计算机。这没有什么问题,只是 strComputer 的值当前为 atl-ws-01,而该计算机没有联机。但您知道吗?实际上,这并不是 什么问题。在我们运行用于连接到远程计算机的代码行之前,我们只需要将 strComputer 的值更改为已联机的 某台计算机即可(例如,我们可以将 strComputer 更改为“.”以便针对本地计算机运行脚本)。您猜该怎么做?是的,这次您说对了,我们就是在命令窗口中做到这一点的:

text

只需在命令窗口中键入相应的命令并按 Enter 键;果不其然,脚本中的 strComputer 的值将更改为圆点 (.)。这真的 很棒!

您也可以输入更复杂的命令。例如,请考虑以下脚本,它用于返回本地计算机上安装的所有服务的名称:

strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer _
    & "\root\cimv2")
For Each objItem in colItems
    Wscript.Echo "Name: " & objItem.Name
Next

非常棒,是吧?美中不足的是缺少了一行代码,该行代码实际上从 Win32_Service 类检索信息。该脚本应该 类似于以下内容:

strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer _
    & "\root\cimv2")
Set colItems = objWMIService.ExecQuery _
    ("Select * From Win32_Service")
For Each objItem in colItems
    Wscript.Echo "Name: " & objItem.Name
Next

不过,没有关系。只需在 Script Debugger 中装载脚本,并分步执行前两行代码。在到达缺少的第三行代码时,不要慌;在命令窗口中键入缺少的代码行,然后按 Enter 键:

text

只需从此处单步执行该代码,脚本就会报告本地计算机上安装的所有服务的名称,这与预想的一模一样。

脚本专家的绝学秘笈

现在告诉您一个真正 精彩的内容。命令窗口被设计为每次运行一行代码;您键入命令,按 Enter 键,就会执行该行代码。这的确不错。但假使您遗漏了 行代码,并且忘了加上 For Each 循环。您一定会想:“没有关系,我逐行键入缺少的代码不就行了。”诚然,有些时候的确可以。但在此处,当您键入缺少的第一行代码,然后按 Enter 键,就会发生以下情况:

text

到底出了什么问题?问题出在您创建了 For Each 循环,但没有 Next 语句(之所以没有 Next 语句,是因为您还没来得及键入呢)。因为命令窗口只处理单行代码,所以您无法键入 Next 语句;在您达到该行代码之前,早就产生错误了。看似您运气不好,对吧?

又错了。实际上,VBScript 允许在一行中键入多行代码,前提是您使用冒号 (:) 来分隔各个行。例如,我们可以将整个 For Each 循环放在一行代码中(如下所示):

For Each objItem in colItems:Wscript.Echo "Name: " & objItem.Name:Next

对,您想到我们前面去了:如果在命令窗口中键入 字符串并按 Enter 键,就会执行所有三行代码:

text

您会用到这些东西吗?可能不会。但我要重申的是,几个月前您会想到您会需要与脚本有关的工具 吗?

结束语

作为人类的一员,我不愿承认,但我不得不说我们可能永远也无法摆脱电脑虫的纠缠。加州秃鹰如何?对,我们是可以将加州秃鹰赶尽杀绝。但电脑虫呢?绝对不可能;它们实在是太多了。

另一方面,单单因为虫子种族不能根除,还不足以说明我们不能追踪并消灭虫子个体,尤其是潜伏在我们脚本内部的那些虫子。如果我们可借助于诸如 Microsoft Script Debugger 之类的工具,那我们还等什么呢?总之,如果那些电脑虫可以使用此类工具来对付我们,后果可就不堪设想了……试一下 Script Debugger,告诉我们您的感受。

发表于 @ 2004年11月27日 08:37:00|评论(loading...)

新一篇: 最全网络协议图  | 旧一篇: (转载)脚本漫谈

用户操作
[即时聊天] [发私信] [加为好友]
Kendiv
订阅我的博客
XML聚合  FeedSky
Kendiv的公告
我们家最坏的就是他~~ 我的mimi,可爱吧~~
2008.10.28
Coding Life
English version
/*---------------------
Personal Info:
Name: Kendiv
----------------------*/
文章分类
收藏
AI
智能中国
AntiVirus
Malware-Research‏
AV Engine
a tool to collect malware
ClamAV for Windows
ClamAV Virus Database
ClamAV-Home
Disassembly Challenges
Filetype Detection - wiki
IceSword - Home
OpenAnti-Virus
BBS、论坛
Juniper Networks
Visual Studio Team System - 微软中文技术社区
ChinaUnix Blog
飞翔,嵌入式linux
Data Loss Prevention (DLP) Security
Hacker
Collection.Sites
Z0MBiE's HomePage
ICAP
ICAP-Forum
POSIX Threads(PThreads)
Open Source POSIX Threads for Win32
Squid
squid-home
Virtualization
虚拟机之家
Web Tech.
Https - wiki
ICP - Home
技术网站
absurd,大牛
ChinaUnix
Cisco Systems
CSDN
DDK MVP Expert Zone
Dr. Dobb's Journal
Ethereal
Kernel Mustard的专栏
Linux Journal
LinuxAid 技术支持中心
Microsoft Systems Journal
Microsoft TechNet
MSDN 技术资源库
MSDN中文WebCast
SysInternals(FreeWare)
The Code Project
VCHelp
VC在线
VC知识库
Windows Driver Programming(WD-3)
ZDNet China
中国协议分析网
哈工大·纯C论坛
安全焦点
微软中国社区
绿盟技术版
罗云彬的编程乐园
驱动开发网
其他
前程无忧
数字北京
清韵书院
铁血军事网
铁血读书
网上书店
China-Pub
第二书店
友情链接
JoyCode博客堂
Rong
嵌入式软件开发网(UCSDN)
狗狗
邹欣的Blog
存档
Csdn Blog version 3.1a
Copyright © Kendiv