您好,脚本专家!本地用户和组的不成熟解决方案

Microsoft 脚本专家

下载这篇文章的代码: HeyScriptingGuy2007_04.exe (151KB)

如何管理文件和文件夹的安全描述符?

信不信由你, 曾经一度(当然那是很久以前),脚本专家们真的并不都是那么聪明。我们知道这难以置信,但是 — 您认为不那么难以置信是什么意思呢?您觉得没有那么久以前又是什么意思呢?我们将会让您知道原因的 — 哦,好的。显然,您曾在几天前看到一位脚本专家作者努力组装一台新电视。

这位特别的脚本专家却还振振有词地辩解说,安装一台新电视不是“火箭科学”;事实上,安装电视比“火箭科学”还要复杂得多。即使仅仅安装电视本身也要涉及三根视频线和一对音频线。除此之外,您还要想办法安装一台 VCR 播放器、一台 DVD 播放器和一个 Xbox®,更不要说在“通用”遥控器上乱按一通数字让遥控器识别所有这些设备了。相比起来,将卫星发射到环绕火星的轨道上简直是小菜一碟。

不寻常的是,脚本专家实际上能够完全正确地连接好这些设备。然而,当他打开所有的设备时,却一点动静都没有。他很负责地再三检查每一处连接。他通读所有的用户手册,甚至上网查询,看看自己是不是漏掉了什么信息。可是什么都没漏掉。他简直都要用他的 MP3 播放器来祭奠电子之神来找到原因了,却突然发现他忘记了一个重要步骤:把分线盒上的电线插入墙上的插座里。

注意: 您可能认为之所以把脚本家族的代码连接起来如此之难是因为脚本家族的内容太多。事实上,脚本专家也同意这一点。不过,每一次这个家族为是否增加更多的内容而投票时,脚本专家总是以 2-0 胜出。不,不是 2-1,而是 2-0。出于某些原因,“脚本爸爸”的选票从未计算在内,而且没有档案记录。

您一定会问为什么。是的,他已经尽力争取了。但迄今为止,联合国拒绝向“脚本家族”派驻选举监督人。

当然,既然我们考虑到这个问题,脚本专家们(尤其是安装新电视的那位)也没有什么,因为他们还不成熟。例如,他们计划编写《Microsoft® Windows® 2000 Scripting Guide》(Microsoft® Windows® 2000 脚本指南),当他们拟定书中章节时,他们向许多专家寻求帮助。其中保留的一章是:管理本地用户和组。“管理本地用户和组?!?”专家们惊讶地说,“这怎么能考虑成为一章呢?谁会关心管理本地用户和组呢?”

噢,令我们感到委屈的是我们发现每个人(也许除了我们内部的“专家”之外)都关心本地用户和组的问题。每周我们收到大量的电子邮件,询问如何在本地组中进行操作,如添加一个用户、删除一个用户或更改计算机上本地管理员密码之类。

当然,在脚本中心脚本存储库中确实有一些示例脚本向您展示如何执行这类任务,但大多数这类脚本都是一次只对一台计算机有影响。这远远不够:人们想知道如何同时在多台计算机上完成这类任务。仅仅在一台破计算机上更改本地管理员密码?回到现实世界来吧,脚本专家 — 我们需要的操作是在所有的计算机上、或在一个 OU 中的所有计算机上、或在我们所有的邮件服务器上、或 — 噢,你终于明白了。

考虑到脚本指南出版于 2003 年,这意味着我们已经身陷这类同样的电邮信息中将近四年了。一位脚本专家最近谈及:“这有另一位读者询问如何从 Excel® 电子表格中读取计算机名称,然后在每一台计算机上更改本地管理员密码。”

嗯,我们花了四年的时间,但我们终于解决了这个问题。图 1 所示正是我们的解决办法。

Figure 1  我们最终解决了问题

On Error Resume Next

Set objExcel = CreateObject(“Excel.Application”)
Set objWorkbook = objExcel.Workbooks.Open(“C:\Scripts\Test.xls”)
objExcel.Visible = True

i = 2

Do Until objExcel.Cells(i, 1).Value = “”
    strComputer = objExcel.Cells(i, 1).Value   
    Set objUser = GetObject(“WinNT://” & strComputer & “/Administrator”)
    If Err = 0 Then
         objUser.SetPassword “egTY634!alK2”
         objExcel.Cells(i, 2).Value = Now
    End If
    Err.Clear
    i = i + 1
Loop

注意: 正如您看到的,我们花了四年的时间并不是因为编写这个脚本很难;我们花了四年的时间写这个脚本是因为我们需要这么长的时间来把事情的方方面面做好。这就是为什么我们绝不想错过脚本专家每年一度的新年晚会,毕竟,您不知道下一次晚会是否还会举行。

我们这个脚本的功能就是从电子表格中提取计算机名称信息,然后更改每台计算机上的本地管理员密码。为了执行这个小程序,我们从简单的情况开始,假定您有一个非常简单的小型电子表格,就如同图 2 所示的一样。(如果没有,就立即创建一个。)

图 2 必备的计算机列表

图 2** 必备的计算机列表 **(单击该图像获得较大视图)

如您所见,这没什么特别的。在第一列(列 A),我们列出了所有要更改密码的计算机名称。在第二列(列 B),我们仅仅是跟踪每台计算机上管理员密码最后一次更改的日期和时间。这样您可以很容易注意到管理员密码是否全都处于同步状态。

至于脚本本身,我们以 On Error Resume Next 语句开始。通常我们并不在脚本中加入错误处理语句,这样会麻烦许多。这并不是因为我们反对错误处理;而是因为我们想让脚本尽量简短并易于跟随。然而在本例中,On Error Resume Next 语句是至关重要的。毕竟我们的脚本将要尝试连接到许多不同的计算机上。如果其中一台计算机关闭会发生什么情况呢?嗯,有了错误处理,一切尽在掌握之中。当然,错误还会发生,但是脚本将能够对它置之不理并继续运行。没有错误处理,您遇到的情形会与那位脚本专家第一次打开新电视时遇到的如出一辙。那就是什么反应都没有。

接着,我们用下面一段代码创建一个 Excel.Application 对象的实例,使这个 Excel 实例在屏幕上可见,并打开电子表格 C:\Scripts\Test.xls:

Set objExcel = CreateObject _
    (“Excel.Application”)
Set objWorkbook = objExcel.Workbooks.Open _
    (“C:\Scripts\Test.xls”)
objExcel.Visible = True

最后,但绝不等于不重要的一步是:我们把值 2 赋给名为 i 的计数器变量。我们将用此变量跟踪电子表格中的当前行。

注意: 那么,为什么 i 的赋值为 2 呢?很简单:如果您仔细看过电子表格,就会发现我们的数据实际上是从第二行开始的。第一行仅仅是一个标题行。

看到了吗?我们也许不擅长连接 YPbPr1 电缆,但是我们确实了解一些脚本方面的知识。

不管怎样,我们有时确实能做到这一点。

现在,我们准备开始工作。首先,我们设置一个 Do Until 循环,运行该循环直到在第一列遇到一个空单元格。是的,这意味着您无论做什么,都不能在第一列留下空行。如果您的脚本遇到了一个空行,它将认为已到达数据的结尾,所以循环(和脚本)将结束。尽管有办法解决空行的问题,但是仅仅避免在您的电子表格的任何位置出现空行会使任务简单得多。

在循环内部,我们开始将第 i 行,第 1 列单元格中找到的值赋给变量 strComputer。(记住,第一次循环时,i 等于 2;因此我们处理的是第 2 行第 1 列单元格中的值。)我们接着使用该行代码为本地管理员帐户创建一个对象引用 (objUser),帐户所在计算机通过变量 strComputer 表示:

Set objUser = GetObject(“WinNT://” & _
    strComputer & “/Administrator”)

如果此脚本出现错误,它将在这里失败。例如,假设网络出了问题、假设计算机关闭或假设一位脚本专家把他的 DVD 播放器安错了地方而使整个美国西部陷入了黑暗。如果出现了这方面的问题,我们将无法连接到这台计算机上。这就是为什么我们立即检查 VBScript Err 对象的值:

If Err = 0 Then

如果 Err 对象等于 0,就是说我们可以毫无问题地连接到这台计算机(以及管理员帐户)。如果 Err 不等于 0,那么就意味着一件事:我们尝试连接失败。在这种情况下,我们大可不必徒劳地去尝试更改那台计算机密码;毋庸置疑,如果我们无法连接,就更无法更改其密码。

假设一切正常,没有错误发生,则我们执行下面两行代码:

objUser.SetPassword “egTY634!alK2”
objExcel.Cells(i, 2).Value = Now

在第一行中,我们使用 SetPassword 方法为本地管理员帐户赋值新的密码 (egTY634!alK2)。(是的,我们知道:您应该不会用您孩子的名字作为密码。)在第二行中,我们把当前的日期和时间(使用 VBScript Now 函数)写到第 i 行第 2 列单元格。注意,仅当我们确实能够连接到远程计算机并更改其密码时,我们才去更新“密码更改”列。如果我们无法连接到一台计算机,则绝不会更新其相应的“密码更改”字段,这就是我们如何了解哪些操作成功和哪些操作不成功的方法。

这一切都意味着什么?这意味着,第一次循环时,我们的电子表格会如图 3 所示。

图 3 重置第一个密码后的计算机列表

图 3** 重置第一个密码后的计算机列表 **(单击该图像获得较大视图)

对计算机 1 的处理就这样结束了。现在,我们需要做一些边角余料的工作,以利再战:

Err.Clear
i = i + 1

尽管这两行代码很短,却非常重要。(脚本专家可不会编写任何不重要的代码!)第一行将 Err 对象重置为 0。如果没有错误发生,这是多余的,毕竟这时 Err 对象已经是 0 了。不过,如果确实有错误发生,那么 Err 对象绝不会等于 0。在这种情况下,我们必须手动重置 Err 对象为 0,这一点至关重要。这是为什么?因为这是 Error 对象:它只跟踪错误,而不跟踪成功的操作。例如,我们假设计算机 1 出了毛病而无法建立连接。为了叙述的方便起见,假设这导致 Error 对象被设为 99。

现在,假设我们进入下一轮循环,并成功连接到计算机 2,您觉得现在 Err 的值将会是什么?没错:不管您是不是愿意,Err 将仍然被设为 99。这是由于只有当错误发生时,Err 对象的值才会改变。如果没有错误发生,Err 对象会无限期的保持其当前值。

这对我们来说真的很重要吗?是的,应该是这样。毕竟,从技术上而言,我们即使成功连接计算机也不会更改其密码;相反地,仅当 Err 等于 0 时我们才会更改密码。而且 — 我们打开计算器的时候要好好想想 — 是的,我们是对的:99 不等于 0(当然,脚本家族选举的时候除外,“脚本爸爸”投 99 票也等于 0 票)。这对我们很重要,因为即使我们连接到计算机 2,Err 也不等于 0。而因为 Err 对象不等于 0,所以我们甚至不会尝试更改本地管理员密码。

换句话说,如果 Err 的值曾经发生变化,则需要将其值重置为 0;否则从此以后您的脚本将认为出现错误。而如何将 Err 对象重置回 0 呢?答对了:只需调用 Err.Clear。

接下来,我们令 i 的值增加 1。为什么?是这样,第一次循环时变量 i 等于 2,是因为我们想连接列表中第 2 行,第 1 列的计算机。第二次循环我们想连接第 3 行,第 1 列的计算机。如果明白了 i 表示我们要处理的那一行,我们就应该令 i 等于 3。每个人都知道,2 + 1 = 3。(幸好我们的计算器还在运行,不是吗?)只有这样,我们才进入下一轮循环并对电子表格中的下一台计算机重复此过程。

现在的结果的确很酷,但是 — 看看我们怎样变得更加成熟 — 我们非常确信,就凭这个脚本本身,我们不会再陷入堆积如山的电子邮件中了。人们会说,“那个 Excel 表格确实不错,但是我还需要更改一个 OU 上的、或一个文本文件中列出的所有计算机的本地管理员密码。或许我喜欢弹出一个计算机列表,并能从列表框中选择要处理的计算机。或许我真的喜欢......”

请放心。您的愿望就是脚本专家的神圣使命。(想想您为了让愿望变为现实,原意等上四年,这确实是我们义不容辞的责任。)事实证明,我们在脚本中心发布了许多的多计算机模板。这些模板使得对多台计算机运行脚本变得非常容易。例如,假如您确实要更改一个 OU 中所有计算机的本地管理员密码。嗨,没问题。对一个 OU 中所有机器运行脚本的模板与图 4 类似。

Figure 4 对一个 OU 运行脚本

On Error Resume Next

Set objOU = GetObject(“LDAP://OU=Finance,dc=fabrikam,dc=com”)
objOU.Filter = Array(“Computer”)

For Each objComputer in objOU
    strComputer = objComputer.CN

    ‘ 
=====================================================================
    ‘ Insert your code here
    ‘ 
=====================================================================

    Set objComputer = GetObject(“WinNT://” & strComputer & “”)
    objComputer.Filter = Array(“User”)
    For Each objUser in objComputer
        Wscript.Echo objUser.Name
    Next

    ‘ 
=====================================================================
    ‘ End
    ‘ 
=====================================================================

Next

看到模板中标着“在此处插入代码”的部分了吗?您所要做的就是删除该部分中的示例代码,并以更改本地管理员密码的代码(这些代码可以从本专栏复制)代替。换句话说,用下列代码代替示例代码:

Set objUser = GetObject(“WinNT://” & _
    strComputer & “/Administrator”)
If Err = 0 Then
    objUser.SetPassword “egTY634!alK2”
End If
Err.Clear

这个具体的例子不会为您记录结果,但您可以很方便地加上您自己的代码。替换示例代码,更改连接字符串 LDAP://OU=Finance,dc=fabrikam,dc=com 以指向您 OU 中的一台计算机,然后便可以开始使用了。

说实话,实际上我们一年多以前就发布这些模板了,而我们觉得这些模板应当是我们在脚本中心发布的所有项目中,最受欢迎的项目之一。我们想错了:尽管人们成天给我们发邮件,询问如何对多台计算机运行脚本,却很少有人使用这些模板。我们假设这是因为没有人意识到这些模板的存在。但现在您知道了。或者如果我们告诉您,您至少知道到哪去找这些模板:microsoft.com/technet/scriptcenter/scripts/templates。嗯,也许这就是为什么从来没有人使用这些模板的原因......

所以,既然脚本专家们成熟多了,这也意味着他们不象以前那样笨拙了吗?让我们这样来解释一下:不是很久以前,一个脚本专家决定用周末的时间修理房前屋后的几个设施。当他周一回来上班的时候,他的大拇指破了,流血不止,而腿上划了一个大口子。

仔细想来,也真的不是那么糟糕。至少对一个脚本专家来说是这样。

Microsoft 脚本专家为 Microsoft 工作,也就是说受雇于 Microsoft。在没有玩/指导/观看棒球(及其他各种娱乐活动)时,他们会管理 TechNet 脚本中心。要查找所需信息,请登录网站 www.scriptingguys.com

© 2008 Microsoft Corporation 与 CMP Media, LLC.保留所有权利;不得对全文或部分内容进行复制.