邪恶八进制信息安全团队技术讨论组's Archiver

心灵港湾 2004-11-29 12:25

[转载]脚本的故事之2004 年 7 月

本页内容
脚本恰似好用的参数
为脚本添加命令行参数
回到刚才那个问题,什么是命令行参数呢?
这很酷,但还可以更酷,如果我们能…
1 是最孤独的数字
计算机上对任一服务进行操作又该如何呢? 那么在任意数量的计算机上对任一服务进行操作又该如何呢?
计算机上对任何服务进行操作,那该怎么办?  不过,如果想在任意数量的计算机上对任何服务进行操作,那该怎么办?
下一步做什么?



脚本恰似好用的参数
大多数人都知道周期蝉 — 人们更常被其为 17 年蝉。周期蝉是一种蜫虫;它们在某个夏天出现,发出大量的噪音,制造巨大的骚乱,然后消失不见,但是 17 年后又会重新出现,重复这一周期。或许我们可以这样说:在许多方面,我们这些 Microsoft 脚本专家就像 Microsoft 的脚本蝉 — 我们突然出现,大显身手,兴风作浪,然后消失。等过了大约 17 年,我们突然又回来了。这个周期会一直循环下去吗?谁知道呢?请到 2021 年登录 TechNet 查找答案吧!

换句话说,我们知道,从上一次脚本的故事 专栏到现在的确 已经很长时间了。这么长时间没露面有许多原因—慢着,谁说的“先坦白承认你们这些家伙只是偷懒而已?”为什么这么说?如果我们不是偷懒的话,我们就会真的给您个…不过请记住,重要的事情不是问“你们到哪里去了?”重要的是我们回来了,并且我们是带着全新的、很酷的“脚本中心”一起回来的。在写这篇文章的时候,我们不清楚这些新特色中有多少已经亮过相,但是您很快就会看到一个扩展后的“脚本中心”,它包括:

• 数百个新脚本。

• 指向网络广播、脚本编写文章、脚本专栏等的链接。

• 新的“解决方案中心”,提供能直接被企业所使用的、由 Microsoft 产品团队开发的脚本。

• 脚本专家 工具包和脚本专家书架,带注释的脚本编写工具与资源列表。

• 新的每日问答专栏您好!脚本专家!


最重要的是,我们将每隔 17 年推出一个新的脚本的故事 专栏。我们保证做到!

好吧好吧。我们退一步,就每月提供一个新的脚本的故事 专栏;再次使脚本的故事 成为脚本编写者的首选资源,方便你们查找简单、易懂的常见脚本编写问题的答案,比如“你们这些人怎么需要这么长时间来写一个小小的脚本专栏?”

_px_up.gif" width=7 border=0>返回页首
为脚本添加命令行参数
我们原先计划在这个月讨论如何在脚本中使用命令行参数。(是的,对专栏来说这的确 是个好主意,不过不用谢我们;毕竟我们花了 17 年才想出这个主题。)您可能已经注意到了,“脚本中心”中的大部分脚本都是为在一台计算机上运行而设计的;除此之外,我们也倾向于对计算机名这样的对象采用硬编码。例如,以下脚本只能完成一个任务:停止本地计算机上的“警报器”服务。如果您看得足够仔细,您会发现我们指定了脚本运行的对象计算机的名称 (.) 和要停止的服务的名称 (Alerter):

strComputer = "."
Set objWMIService = GetObject _
   ("winmgmts:\\" & strComputer & "\root\cimv2")
Set colServices = objWMIService.ExecQuery _
   ("Select * FROM Win32_Service WHERE Name = 'Alerter'")
For Each objService in colServices
   objService.StopService
Next

不用说,让脚本只能在本地计算机上停止“警报器”服务看起来并不很有用,也没什么可兴奋的。那么,我们为什么要写这样的脚本呢?这至少有两个原因。其中之一是,硬编码值有助于缩短脚本的长度。我们的脚本要富有指导性,我们不希望因为有许多附加代码而使脚本显得混乱;我们希望方便您将注意力集中到手头的任务上。同样因为这个原因,我们的脚本中通常不包括错误处理;对我们的目的来说,脚本越短越好。(此外,看一看我们编写简单脚本所花的时间;您能想象编写复杂脚本要花多长时间吗?)

另一个原因是,编写简单脚本更便于您利用我们的脚本和根据自己的需要来修改脚本。例如,假如我们的所有脚本都从一个文本文件读入计算机名,执行某些任务,然后将数据输出到 Excel。这么做没问题;但是这就不利于您利用该脚本并将其修改为从 Active Directory 读入计算机名,然后将信息输出到命令窗口。我们的脚本写得越复杂,您就越难自定义这些脚本来满足您的独特需求。反过来,这也使我们有点像解决方案供应商,而不像是教育者;请相信我们,没有人希望这样。(当然,除非您愿意为一个解决方案等待 17 年。)

总的来说,这已被证明是一个有效的方法。但是必须承认,我们的确 有一个较坏的说话习惯,比如,我们会说,“这确实只是一个脚本骨架,但是您可以很方便地修改它以使其执行接受命令行参数等操作。”的确如此,只是我们好像从来不准备告诉别人怎样 修改我们的示例脚本,让它们执行接受命令行参数等操作。我们将马上注意弥补这样的疏漏。(你们已经埋怨过我们很懒了…)

_px_up.gif" width=7 border=0>返回页首
回到刚才那个问题,什么是命令行参数呢?
为了保证我们的理解是一致的,有必要给出一个简单的定义。命令行参数是在您启动实用工具(可以是脚本、可执行文件或任何其他事物)时传递给实用工具的附加信息。例如,假设您试图 ping IP 地址 192.168.1.1。在这种情况下,您将在命令提示符下键入类似以下的内容:

ping 192.168.1.1

您也许已经猜到了,192.168.1.1 就是一个命令行参数(有时候叫做命令行变元,有时候叫做命令行开关,有时候直接叫做参数)。为什么您需要 命令行参数呢?好的,实际上您不需要。但是拿 Ping 来举例吧。假如您不能将 IP 地址作为命令行参数来传递,那么 Ping 对您有什么用处呢?Ping 是一个有用的实用工具,因为它允许您 ping 任何 IP 地址;它能这么做是因为它没有将任何 IP 地址硬编码在其中。相反,您在每次运行 Ping 的时候都要指定一个 IP 地址作为命令行参数。如果不能接受命令行参数,至少 Ping 的用途会很有限。

脚本也有同样的问题。如果您将计算机名称硬编码在了脚本中,那么只能在这台计算机上执行您的脚本。如果您需要在另一台计算机上执行脚本会怎么样呢?在这种情况下,您要么必须修改脚本,要么创建一段全新的脚本以便在第二台计算机上运行。不用说,这两种方法的效率都不高。更好的方法是创建一个脚本,其中包含了便于该脚本在任何 计算机上运行的方便方法。这正是命令行参数的设计思想。

我们知道,有些读者看到这里开始有点害怕了;你们会想“命令行参数?好像很难啊。我们才 17 年没见面而已,再等等好吗?”听着,您根本不必担心;因为您会发现命令行参数很容易掌握。事实上,您可以立即将命令行参数传递到脚本。在记事本中键入以下只有一行的脚本,然后将其保存为 args.vbs。

Wscript.Echo strComputer

现在,在命令提示符下用以下命令运行该脚本:

cscript args.vbs atl-ws-01

看看发生了什么情况。哦,什么也没发生;不过,如果您是脚本的故事 的读者,您应该对这种现象很习惯了。为什么除了屏幕上出现空白的信息反馈外没有出现任何其他现象呢?很简单,strComputer 变量没有设成任何值。但是,现在您不必再担心这种情况了。重要的是,您已经将一个命令行参数 (atl-ws-01) 传递到了脚本,并且脚本接受了该参数且没有出现任何问题。换句话说,Windows Script Host 无需任何特殊编码就接受了命令行参数;您唯一要做的就是在代码中加入一两行,让脚本使用 这些参数。

顺便说一下,您可能希望知道,当启动这个脚本并向它传递了一个命令行参数的时候,“这个参数到底 经历了些什么?它仅仅是消失了吗”答案是否定的,它不是仅仅消失了而已。事实上,传递给脚本的所有命令行参数都保存在 WSH Arguments 集合中 — 在您每次运行脚本的时候,都会自动创建这个集合。Arguments 集合是一个在运行时由传递给脚本的所有命令行参数组成的数组。当您启动脚本的时候,参数 atl-ws-01 实际上保存在 Arguments 集合中;只是我们的脚本中没有任何代码从这个集合中检索 它的值而已。只要给我们一秒钟,我们就可展示如何在您的脚本中添加此种代码。

注意 在进一步讨论之前,我们想指出,在 WSH 中,参数是用空格分隔的。假设我们键入以下内容:

cscript args.vbs atl-ws-01 atl-ws-02

在这个例子中,我们有两个参数:atl-ws-01 和 atl-ws-02。假设我们键入以下内容:

cscript args.vbs atl-ws-01/atl-ws-02/atl-ws-03

这里有多少个参数?不错;它只是一个大参数:atl-ws-01/atl-ws-02/atl-ws-03。因为参数之间必须用空格分隔。要想传递三个参数,我们必须键入以下内容:

cscript args.vbs atl-ws-01 atl-ws-02 atl-ws-03

如果像下面的例子中那样在参数之间键入多个空格,那么会怎样?

cscript args.vbs atl-ws-01      atl-ws-02 atl-ws-03

没问题;WSH 会自动丢弃多余的空格。但是,如果参数中必须包含空格,比如传递文件夹名称 C:\Documents and Settings,那么会怎样?在这种情况下,请在参数外面加双引号,比如:

cscript args.vbs atl-ws-01 "C:\Documents and Settings"



好了,现在回过头来讨论我们的参数发生了什么事情?刚才说过,参数是保存在数组中的;这个数组同大多数数组一样,给数组中的每一项分配一个索引号:第一项的索引号是 0,第二项是 1,依此类推。就脚本而言,数组类似下表:

索引号 值
0
atl-ws-01

1
atl-ws-02

2
atl-ws-03


您为什么关心这个?信不信由您,您现在已经知道该怎么访问命令行参数了。请看下面这个脚本:

strComputer = Wscript.Arguments.Item(0)
Wscript.Echo strComputer

在记事本中键入上面的脚本,保存为 args.vbs,然后用下面的命令运行该脚本:

cscript args.vbs atl-ws-01

您猜发生了什么?这一次,该脚本返回了值 atl-ws-01。此外,它还会返回您传递给它的任何 值。不相信吗?用这个命令启动此脚本,自己看看结果吧!

cscript args.vbs this_is_my_command_line_argument

而且,您可以使用任何 命令行参数来启动该脚本并查看结果。屡试不爽!

所以我刚才说别怕;因为太简单了。如果您仔细查看这个脚本,您会发现第一行代码读取变量 strComputer 并给它赋值 Wscript.Arguments.Item (0)。Wscript.Arguments.Item (0) 是什么呢?它是 Arguments 集合中的第一项(索引号为 0 的那一项)。换句话说,strComputer 被设为我们输入的第一个参数。您想创建一个可以在任何 计算机上运行的简单脚本吗?请看下面的例子:

strComputer = Wscript.Arguments.Item(0)
Set objWMIService = GetObject _
   ("winmgmts:\\" & strComputer & "\root\cimv2")
Set colServices = objWMIService.ExecQuery _
   ("Select * FROM Win32_Service WHERE Name = 'Alerter'")
For Each objService in colServices
   objService.StopService
Next

明白它的原理了吗?在“脚本中心”的许多示例脚本中,第一行代码经常是这样的:

strComputer = "."

此代码将一个点 (.) 分配给变量 strComputer;我们是用 WMI 脚本实现这一点的,因为在 WMI 语言中,点代表本地计算机。这意味着,默认情况下,我们的大多数脚本都是在本地计算机上运行的。

在我们修改后的脚本中,我们所做的只是一点小小的修改:我们不是将点号值分配给变量 strComputer,而是将键入的第一个命令行参数值分配给它。您想在名为 redmond-ws-87 的计算机上运行该脚本吗?如果想,则只需键入以下命令来启动脚本:

cscript args.vbs redmond-ws-87

想在本地计算机上运行该脚本吗?如果想,则只要将点号用作命令行参数就可以了:

cscript args.vbs .

如果您认为这不值得等 17 年的话,那我们就不知道什么值得等这么长时间了。

_px_up.gif" width=7 border=0>返回页首
这很酷,但还可以更酷,如果我们能…
是的,您说的没错。我们现在有一个脚本可以停止世界上任何一台计算机上的“警报器”服务(当然,前提是我们必须有那台计算机的本地管理员权限)。这没问题,但是我们敢打赌系统管理员不会花那么多时间来停止他们的计算机上的“警报器”服务。真正美妙的是,可以用一个脚本停止任何 计算机上的任何服务。但是这可能吗?

这个问题让我们花了 17 年的时间,最后我们找到了答案:是的,这的确 是可能的。但是,在介绍怎么实现之前,我们先进行个小测试。当我们使用以下命令启动脚本时,您认为 WSH 对参数进行了哪些操作:

cscript args.vbs atl-ws-01 cisvc

没错;就像所有其他脚本一样,这两个命令行参数是被自动加入 Arguments 集合的。这意味着此脚本的 Arguments 集合类似下表:

索引号 值
0
atl-ws-01

1
cisvc


令人难忘,对吗?不过别急,还没完呢。几分钟前,我们刚刚介绍了怎样用类似下面的代码来访问第一个命令行参数:

strComputer = Wscript.Arguments.Item(0)
Wscript.Echo strComputer

这没什么奇怪的:我们只是将第一个参数(索引号 0)的值赋予 strComputer 变量。那么,您认为我们会怎样利用第二个 命令行参数(索引号 1)呢?没错;我们只需将它的值赋予第二个变量:

strComputer = Wscript.Arguments.Item(0)
Wscript.Echo strComputer
strService = Wscript.Arguments.Item(1)
Wscript.Echo strService

用命令 cscript args.vbs atl-ws-01 cisvc 运行这个 脚本时,返回的输出如下:

atl-ws-01
cisvc

您发现这里面的方式了吗?假如我们将 199 个命令行参数传递给脚本;您怎么访问最后那个参数呢?您可能会使用以下这样的代码:

strLastArgument = Wscript.Arguments.Item(198)
Wscript.Echo strLastArgument

为什么是 Wscript.Arguments.Item (198) 呢?记住,Arguments 数组中的第一项是索引号 0;第二项是索引号 1。如果依此类推,那么数组中的第 199 项的索引号是 198。因此是 wscript.arguments.item (198)。如果需要列表中的第 37 项参数,那是什么呢?是 Wscript.Arguments.Item (36)。

用这个办法,可以很快地创建一个脚本来停止任何计算机上的任何服务。我们需要做的只是:

• 加入将服务名称分配给变量 strService 的代码。

• 在 WHERE 子句中引用 strService。


完成后的代码类似于:

strComputer = Wscript.Arguments.Item(0)
strService = Wscript.Arguments.Item(1)
Set objWMIService = GetObject _
   ("winmgmts:\\" & strComputer & "\root\cimv2")
Set colServices = objWMIService.ExecQuery _
   ("Select * FROM Win32_Service WHERE Name = '" & strService & "'")
For Each objService in colServices
   objService.StopService
Next

注意 的确,这里的 WHERE 子句有点奇怪,它带有单引号、双引号、& 号和各种奇怪的标点。如果您不清楚这种“字符串连接”的工作方式,请参见 Microsoft Windows 2000 Scripting Guide(Microsoft Windows 2000 脚本编写指南)中的_vbs_pqcl.mspx" target=_blank>这段引文。


如果想使用 3 个 参数,我们该怎么办?例如,假设我们希望允许使用者选择启动某项服务和停止这项服务。那么我们可以用类似以下的命令来启动脚本:

cscript args.vbs atl-ws-01 cisvc start

使用 3 个参数的脚本看起来就像这样:

strComputer = Wscript.Arguments.Item(0)
strService = Wscript.Arguments.Item(1)
strAction = Wscript.Arguments.Item(2)
Set objWMIService = GetObject _
   ("winmgmts:\\" & strComputer & "\root\cimv2")
Set colServices = objWMIService.ExecQuery _
   ("Select * FROM Win32_Service WHERE Name = '" & strService & "'")
For Each objService in colServices
   If strAction = "start" Then
      objService.StartService
   ElseIf strAction = "stop" Then
      objService.StopService
   End If
Next

请注意,要采取的操作 (start) 是传递给脚本的第三个参数,因此我们将 Wscript.Arguments.Item (2) 分配给变量 strAction。

顺便提一下,只要您按正确的顺序输入参数,这个脚本就能很好地工作。例如,请看用下面的命令启动此脚本会发生的情况:

cscript args.vbs start atl-ws-01 cisvc

是的,这个脚本将尝试连接到名为 start 的计算机,这样就开始出问题了。有什么方法可以解决这个问题吗?事实上有一个办法,我们将立即展示如何使用此方法解决问题。

_px_up.gif" width=7 border=0>返回页首
1 是最孤独的数字
毫无疑问,能将一个计算机名传递到脚本会让您感到高兴,现在您也许在想能不能将多个 计算机名传递到这个脚本。换句话说,您也许想写下面这样的代码:

cscript args.vbs atl-ws-01 atl-ws-02 atl-ws-03 atl-ws-04

然后您会希望在这 4 台计算机上运行此脚本:

• atl-ws-01

• atl-ws-02

• atl-ws-03

• atl-ws-04


这可能吗?当然可以,不过方法可能与您想象的不一样。您会想“atl-ws-01 是数组中的第一项,可以通过调用 Wscript.Arguments.Item (0) 来获得,atl-ws-02 是数组中的第二项,可以通过调用 Wscript.Arguments.Item (1) 来获得,依此类推…”如果您是这么想的,请赶紧打住:这是行不通的。

从技术上讲,这也许 行得通,但是写代码时将是恶梦一场。如果有人只键入三个计算机名会怎么样呢?在这种情况下,如果没有正确的错误处理代码,当脚本试图访问列表中不存在的第四项 Wscript.Arguments.Item (3) 时,脚本会崩溃。如果用户键入了 5 个、50 个或者 500 个计算机名,那会怎么样呢?请相信:试图按参数的索引号来引用参数真的是一个恶梦。

幸运的是,有一种更好的办法可以达到这个目的。还记得我们说过参数是保存在 Arguments 集合 中的么?了不起,是吧?事实上,这的确 了不起;如果您回顾第一天学的“脚本编写 101”,您就知道可以用 For Each 循环来访问集合中的所有参数。请在记事本中键入这段代码,然后将其保存为 args.vbs。

For Each strArgument in Wscript.Arguments
   Wscript.Echo strArgument
Next

然后用下面的命令运行这个脚本:

cscript args.vbs atl-ws-01 atl-ws-02 atl-ws-03 atl-ws-04

现在返回的结果是什么?没错,尽管您连一个索引号都没有引用,脚本还是响应了您的每个命令行参数:

atl-ws-01
atl-ws-02
atl-ws-03
atl-ws-04

接下来我们将做一点具体的事情。假设您需要一个能停止任何数量计算机上的“警报器”服务的脚本。您只需要问:

For Each strArgument in Wscript.Arguments
   strComputer =  strArgument
   Set objWMIService = GetObject _
      ("winmgmts:\\" & strComputer & "\root\cimv2")
   Set colServices = objWMIService.ExecQuery _
      ("Select * FROM Win32_Service WHERE Name = 'Alerter'")
   For Each objService in colServices
     objService.StopService
   Next
Next

正如您所见的,就这么简单。我们创建的 For Each 循环对整个 Arguments 集合执行循环。循环内的第一行代码将当前参数的值赋予变量 strComputer,用它表示我们连接的第一台计算机。当我们运行脚本时,脚本获得第一个参数“atl-ws-01”,并将它赋予变量 strComputer。接下来,脚本连接到这台计算机,停止“警报器”服务;然后执行循环,判断集合中是否还有其他参数。

实际上,我们的确还有其他参数。所以,脚本会获得第二个参数“atl-ws-02”,并将它的 值赋予变量 strComputer。在执行完所有参数之前,这个过程会一直循环下去。执行完毕之后,它会退出 For Each 循环;这样,它就完成了操作。

这虽然很简单,但几乎可以很方便地改编任何 脚本,使之能够针对任意数量的计算机运行。

_px_up.gif" width=7 border=0>返回页首
那么在任意数量的计算机上对任一服务进行操作又该如何呢?
那是什么?恐怕有人会这么问。好奇的读者想知道我们能不能创建一个可停止任何数量计算机上的任何服务的脚本。我们能吗?嘿嘿,我们是“脚本专家”,没有我们做不到的事。(只要再给我们 17 年,我们就肯定能做到。)此类脚本要复杂得多,不过让我们试一试。

首先必须承认:我们对您有所隐瞒。我们说过 WSH 有一个 Arguments 集合,的确如此。(您可以查一查。)但是,WSH 还有两个我们没有提到的其他 Argument 集合:Named 和 Unnamed 集合。如果您想做点异想天开的事情 — 比如停止任意数量计算机上的任何服务,最方便的实现方法是使用这两个没有提到过的集合。

那么,什么是命名参数和未命名参数呢?请考虑下面这个简短的命令行字符串:

cscript args.vbs /service:alerter atl-ws-01

注意,这个例子中,我们有两个参数:/service:alerter 和 atl-ws-01。如果您运行脚本来查看 Arguments 集合中有什么,您会得到以下两项:/service:alerter 和 atl-ws-01。

但是请注意第一个参数的格式:/service:alerter。我们为什么这样传递此参数呢?原因是,WSH 被设计为使用命名参数。使用命名参数时,参数的格式为 /argument_name:argument_value。换句话说,在这个示例中,我们有一个名为 service 的参数,它的值是 alerter。

我们应该注意这一点吗?当然应该。请看下面这段代码:

Wscript.Echo Wscript.Arguments.Named("service")

会出现什么情况呢?我们将返回名称为 service 的参数的值。在运行时,我们将返回 alerter 这一值。明白它怎样运行了吗?假设我们刚才用下面的命令启动这个脚本:

cscript args.vbs /service:cisvc atl-ws-01

会返回什么呢?没错,是 cisvc。这是因为,用这个命令字符串,名称为 service 的参数的值是“cisvc”。

感到困惑吗?不必困惑,这实际上很简单。假设我们用下面的命令启动一个脚本:

cscript args.vbs /service:cisvc /computer:atl-ws-01

那么命名参数集合应该是这样的:

名称 值
service
cisvc

computer
atl-ws01


我们可以在脚本中用类似下面的代码来引用这些参数:

Wscript.Echo Wscript.Arguments.Named("service")
Wscript.Echo Wscript.Arguments.Named("computer")

练一练这些操作,您会发现它的确是有意义的。

_px_up.gif" width=7 border=0>返回页首
不过,如果想在任意数量的计算机上对任何服务进行操作,那该怎么办?
请耐心一点。您需要知道在下面这个命令中命名参数和未命名参数是如何帮助我们的:

cscript args.vbs /service:alerter atl-ws-01

在进行更深入的讨论之前,用下面这样的代码可以很方便地获得 service 参数:

Wscript.Echo Wscript.Arguments.Named("service")

但是如何获得附加在后面的计算机名呢?

其实比您想象的还要简单。要想获得计算机名,我们只需引用 WSH 未命名 Arguments 集合。(您会注意到 atl-ws-01 就是一个没有名称的参数。)我们能给这个参数命名吗?当然。我们可以用下面的方法来命名:

cscript args.vbs /service:alerter /computer:atl-ws-01

这样一来,停止任何一台 计算机上的任何一项服务就很简单了。但是请记住,我们希望停止多台 计算机上的某项指定的服务。我们真正需要的是能够键入下面这样的命令:

cscript args.vbs /service:alerter atl-ws-01 atl-ws-02 atl-ws-03 atl-ws-04

可是,接下来怎样引用所有 4 台计算机呢?最简单的方法是使用 WSH 未命名 Arguments 集合。例如,键入下面的代码并将其保存为 args.vbs:

For Each objArgument in Wscript.Arguments.Unnamed
   Wscript.Echo objArgument
Next

然后用下面的命令运行这个脚本:

cscript args.vbs /service:alerter atl-ws-01 atl-ws-02 atl-ws-03 atl-ws-04

发生什么事情了?您看到了:只返回 4 个计算机名称,就是 4 个未命名参数代表的计算机名。为什么没有返回 service 参数呢?很简单:它是一个命名 参数,这意味着它不是未命名参数集的组成部分。(如果您是对 Wscript.Arguments.Named 集合执行了循环,则会显示 service。)

为了保证每个人都能跟上学习进度,让我们了解一下各种 Argument 集合。以下是 Arguments 集合包含的内容,这个集合由包括命名参数和未命名参数在内的所有参数组成:

Arguments 集合
/service:alerter

atl-ws-01

atl-ws-02

atl-ws-03

atl-ws-04


明白了?命名的 Arguments 集合是这样的:

命名的 Arguments 集合 值
service
alerter


请注意,命名参数包括两部分:名称和值。

最后,未命名的 Arguments 集合是这样的:

未命名的 Arguments 集合
atl-ws-01

atl-ws-02

atl-ws-03

atl-ws-04


信不信由你,我们现在已经胜利在望了。在记事本中键入下面的脚本并将其保存为 args.vbs:

Wscript.Echo Wscript.Arguments.Named("service")
For Each objArgument in Wscript.Arguments.Unnamed
   Wscript.Echo objArgument
Next

然后用下面的命令运行这个脚本:

cscript args.vbs /service:alerter atl-ws-01 atl-ws-02 atl-ws-03 atl-ws-04

变!这个脚本首先返回命名参数 service 的值,然后返回 4 个未命名参数的值。现在,我们已经找到了以下问题的答案:“我可以写一个能在任意数量的计算机上停止任何服务的脚本吗?”您当然可以:

strService = Wscript.Arguments.Named("service")For Each strArgument in Wscript.Arguments.Unnamed strComputer = strArgument Set objWMIService = GetObject _ ("winmgmts:\\" & strComputer & "\root\cimv2") Set colServices = objWMIService.ExecQuery _ ("Select * FROM Win32_Service WHERE " & _ "Name = '" & strService & "'") For Each objService in colServices objService.StopService NextNext

还是很简单吧。首先,我们获得命名参数 service 的值,并将这个值保存在变量 strService 中。然后创建一个 For Each 循环,对所有未命名参数执行这个循环;在这个示例中,所有未命名参数刚好代表我们要运行此脚本的所有计算机。每次运行这个循环时,我们都会获得一个计算机名,并将它分配给变量 strComputer。每次循环也会运行脚本的主体,它执行以下操作:

• 连接到 strComputer 指定的计算机。

• 停止 strService 指定的服务。


很美妙,不是么?

如果您有兴趣,使用命名参数也可以解决用户按不同的顺序传递参数的问题。例如,假设一个用户用下面的命令启动一个脚本:

cscript args.vbs /service:alerter /computer:atl-ws-01

另一个用户用下面的命令启动相同的脚本,请判断两者之间的区别:

cscript args.vbs /computer:atl-ws-01 /service:alerter

没有,没有任何区别。为什么?记住,使用命名参数时,我们使用的是名称,而不是索引号。在这种情况下,我们寻找的是名称分别为 computer 和 service 的参数;我们不考虑它们的传递顺序。使用命名参数时,索引号是不相干的。对未命名参数来说,情况并不总是如此。

_px_up.gif" width=7 border=0>返回页首
下一步做什么?
这就是使用命令行参数能完成的所有任务吗?不,远非如此。例如,有许多方法可以测试是否存在某个特定的命令行参数;如果不存在的话,可以提示用户输入一个参数。(毕竟,一个能够在用户忘记指定要停止的服务时帮助用户停止服务的脚本是很有用的)由于篇幅有限,我们这次无法详细介绍所有这些功能,但是您可以阅读 Microsoft Windows 2000 Scripting Guide(Microsoft Windows 2000 脚本编写指南)的_wsh_ywsa.mspx" target=_blank>相关部分,其中详细介绍了命令行参数,并详细介绍了如何最好地在脚本中利用命令行参数。

同时,我们将开始准备下个月的专栏。毕竟,2021 在不知不觉中就会来到的。

页: [1]
© 1999-2008 EvilOctal Security Team