VBS脚本运行库手册之一——磁盘操作(
文件系统管理是系统管理的一个关键性任务。WSH 和 VB 脚本在这个领域都没有提供相关的功能。幸运的是,你可以通过 script runtime
库来管理这些关键的系统组件,比如磁盘,文件夹和文件。这两种主要的脚本语言,VB 脚本语言和 JS 脚本语言起初的时候都是设计给客户端来编写 IE 的。正是因为如此,这两个脚本语言都有一些局限性。例如VB 脚本和JS 脚本内部都没有提供访问文件管理的一些任务,比如:复制、移动、删除文件等。这样做的目的是为了保护消费者。大多数的的网站不可能让用户通过网页去删除它们硬盘上的东西。尽管如此,脚本已经从类似的HTML 的按钮之类的开始迅速的发展了。随着 ASP 的到来,浏览器的编写者要求在 server 上管理文件。而随着 WSH 的到来,系统管理员要求有在 web 浏览器外来管理文件了。
为了满足这个要求,微软发布的了 Script Runtime Library。它是一个单独的 DLL 文件,scrrun.dll 文件,用来给脚本的编写者们提供一些文件管理的能力,包括以下内容:
1、获取文件系统的信息,包括获得磁盘、文件和文件夹上的信息。
2、复制,移动和删除文件及文件夹
3、创建和读写文本文件。
除了这些功能之外,Script Runtime Library 提供了创建字典的能力。也可以用来对脚本进行编译,使脚本成为加密的。
Note:
这章只是讨论 FileSystemObject 和 Dictionary 的部分,对于 Encode 部分不作讨论,Script Runtime Library
也是 windows 2000 的一部分,它在你安装或者升级微软的应用程序的时候也随着升级和安装了:这些应用程序有以下几个:
1、WSH 2、VB 脚本 3、IE 浏览器 4、Microsoft Office 5、FileSystemObject
正如它的名字一样,FSO 设计用来帮助你管理文件系统,FSO
允许你获取重要的文件组件信息,包括硬盘、文件夹、文件;也提供一般的系统管理的任务,比如复制、删除、移动文件和文件夹。此外FSO 允许你读写文本文件。
用 FSO 其实有些用词不当,因为实际上 FSO 包含很多个对象,每个对象被设计成用来完成特定的目的。组成 FSO
的对象列在了下表中:
Drive 代表系统中磁盘或者磁盘的集合
File 代表系统中的一个文件或者文件的集合
Folder 代表文件系统中的一个文件夹或者多个文件夹
TextStream 代表文本文件中的读取、写入或者提交字符串
※ 磁盘管理
磁盘管理是系统管理的一个重要的部分。作为一个系统管理员,知道安装在这个计算机上磁盘是十分重要的,当然,知道这个磁盘的属性、磁盘类型、子盘大小、总共和剩余空间也是同等重要的。作为一个脚本的编写者,你有两个主要的选择来管理磁盘:WMI
& FSO。一般来讲,WMI 更是管理磁盘的首选项,主要有以下几个原因:1、WMI 能返回一些 FSO 无法返回的属性,比如磁道、扇面等。
2、FSO 不能返回一个目标磁盘。而是返回所有的磁盘,然后进行筛选出你感兴趣的磁盘。而你可以用 WMI 通过磁盘符号来返回特定的磁盘,WMI 可以用来返回远程计算机上磁盘的信息,但是 FSO 不可以,除非它用 wshcontroller对象。
虽然 WMI 或许是获得磁盘信息的最好工具。但是这里至少有两个原因使你必须去熟悉 FSO。
第一:因为有些低端的操作系统无法安装 WMI,所以只能使用 FSO;
第二:最重要的一点是因为:脚本的编写者们在想到获取磁盘信息的时候就会用到 FSO,而你作为系统管理员,可能会来看其它人的脚本,这样就需要了解 FSO。注:外国人写的真是拗口,我理解这句话的意思就是说FSO比WMI应用的更广泛一些。
※ 如何返回磁盘信息
在你管理计算机的磁盘之前,你需要知道你的计算机上实际上有多少磁盘可以使用。FSO 允许你管理安装在计算机上的所有磁盘,包括移动硬盘和网络硬盘(就是只要是有盘符的就可以了)为了返回这个集合,你需要首先创建一个 FSO 实例,然后创建磁盘属性的对象。当这个集合返回后,你可用一个 For Each 的循环来重述这个集合。
例如,下面的脚本就是要返回安装在本地计算机上的磁盘集合然后返回它们的盘符:
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set colDrives = objFSO.Drives
For Each objDrive in colDrives
Wscript.Echo "Drive letter: " & objDrive.DriveLetter
Next
※如何绑定指定磁盘
如果你事先知道要绑定的磁盘(例如C 盘或一个共享文件夹\\accounting\receivables),你可以用 GetDrive
的方法来直接绑定磁盘。这样你可以不用去筛选就获得特定磁盘的信息。GetDrive 需要一个单独的参数:磁盘符号或者是共享文件夹的 UNC 路径。你可以用如下的几种格式:C C: C:\
在下面的脚本中首先创建一个 FSO 对象,然后用 GetDrive 的方法来直接绑定C盘,然后回显可用的磁盘空间:
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objDrive = objFSO.GetDrive("C:")
Wscript.Echo "Available space: " & objDrive.AvailableSpace
注意,这里没有 For Each
循环,你就可以获得磁盘的属性。因为脚本是返回一个单独的磁盘对象,而不是所有对象的集合。因此也就不用筛选了。
※如何列举磁盘驱动器的属性
磁盘集合的作用总是用来监视和列举的。作为一个系统管理员,你需要知道计算机上的可用磁盘,也需要知道磁盘的序列号、可用空间等信息。在你获得当前磁盘的集合或者单个磁盘的时候,你就可以获得下表列出的所有属性。
1、AvailableSpace——报告磁盘上可以使用的空间,单位是 bytes。如果想以 kb 为单位返回,除以 1024,如果想以 M 为单位返回,则除以
1.048,576。这个属性返回的是可用的磁盘空间的总数。如果一个磁盘启用了磁盘限额,那么他返回的数字可能比实际的要小。
2、DriveLette——返回特定磁盘的盘符。这个盘符不包含那么冒号。例如软盘返回的是A,而不是A:
3、DriveType——整数的值代表不同的磁盘类型:1:移动硬盘 2:硬盘 3:网络硬盘 4:CD-ROM 5:RAM 磁盘
4、FreeSpace——返回磁盘剩余空间的总额。单位是 bytes,如果是返回的单位是 kb,就除以 1024,如果是以 M 为单位,除以
1,048,576。注意:这个和磁盘可用空间不一样,剩余的磁盘空间不会考虑磁盘限额。
5、FileSystem——返回文件系统的类型(FAT,FAT32,NTFS)
6、IsReady——标识磁盘是否可以访问。这个值来判断软盘和CD-ROM是否为空
7、Path——磁盘路径。对于本地磁盘,返回盘符和脱尾符(就是那个冒号),比如A:对于网络磁盘,就返回它的 UNC 路径。
8、RootFolder——返回磁盘根目录下的文件夹。
9、SerialNumber——磁盘的生产商给磁盘分配的序列号。对于软盘和网络硬盘来说,它们的值是 0。
10、ShareName——分配给网络驱动去的共享名称。
11、TotalSize——报告整个的磁盘空间,单位是 bytes,如果想以 kb 为单位,除以 1024,如果想以 M 为单位返回,则单位为 1,048,576。
12、VolumeName——分配给磁盘的卷名。
为了列举安装在计算机里的磁盘的属性,创建一个 FSO 实例,然后创建一个 FSO 属性的对象,用 For Each 循环,来重现各个磁盘的配置。对于集合当中的每个磁盘,你可以返回一个或者的属性。如下列脚本列举出安装在计算机上所有磁盘的所有属性。
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set colDrives = objFSO.Drives
For Each objDrive in colDrives
Wscript.Echo "Available space: " & objDrive.AvailableSpace
Wscript.Echo "Drive letter: " & objDrive.DriveLetter
Wscript.Echo "Drive type: " & objDrive.DriveType
Wscript.Echo "File system: " & objDrive.FileSystem
Wscript.Echo "Is ready: " & objDrive.IsReady
Wscript.Echo "Path: " & objDrive.Path
Wscript.Echo "Root folder: " & objDrive.RootFolder
Wscript.Echo "Serial number: " & objDrive.SerialNumber
Wscript.Echo "Share name: " & objDrive.ShareName
Wscript.Echo "Total size: " & objDrive.TotalSize
Wscript.Echo "Volume name: " & objDrive.VolumeName
Next
上述脚本有个潜在的缺点,就是如果没有软盘或者光驱里面没有光盘,会产生一个 disk not ready 的错误出来。当磁盘没有准备好的时候用 FSO 来访问会有些错误的。虽然 FSO 可以标识那些没有准备好的磁盘,但是你访问它的一些属性的时候会报错出来,比如 AvailableSpace 或 FreeSpace。如果磁盘没有准备好,你只可以访问如下的四个属性:DriveLetter、DriveType、IsReady、ShareName。所有尝试访问其它属性都会报错。
幸运的是,IsReady 属性允许你用脚本来检查一个磁盘是否准备好了。IsReady 返回一个 Boolean 值,如果这个值是true,则磁盘准好了,你可以访问它的所有属性,否则如果这个值是false,则磁盘没有准备好,只能返回上面的四个属性了。可以对每个磁盘,用 IsReady 属性来保证磁盘是准备好的。如果是就返回磁盘的盘符和剩余空间,如果不是就只返回磁盘的盘符。
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set colDrives = objFSO.Drives
For Each objDrive in colDrives
If objDrive.IsReady = True Then
Wscript.Echo "Drive letter: " & objDrive.DriveLetter
Wscript.Echo "Free space: " & objDrive.FreeSpace
Else
Wscript.Echo "Drive letter: " & objDrive.DriveLetter
End If
Next
Note:这个问题不会发生在 WMI 上(如果软盘或者光驱中没有盘,脚本就会失败),WMI 只是认为返回的空间为 Null 。
VBS脚本运行库手册之二——管理文件夹
(※管理文件夹
磁盘的属性比如磁盘的剩余空间,磁盘的总额提供了一个全局的信息给系统管理员。但是,对于系统管理而言,磁盘的信息虽然是必须的,但是还不够。知道文件存储在哪个盘固然重要,但是你也需要知道文件是存储在哪个文件夹下的。此外许多的系统管理任务都是发生在文件夹级别的,比如复制,移动,删除和列举文件夹的内容。
FSO 可以返回一个磁盘上文件夹的全部信息。此外,FSO 提供了一系列的方法来完成诸如:复制、移动、删除文件夹的操作。
※ 如何绑定指定的文件夹
在微软的调用文件中,文件夹是 COM
对象。这就是说在你访问一个单独的文件夹的属性时,你必须去创建一个文件夹的对象reference,这个过程叫做绑定。你可以用FSO和GetObject的方法来绑定到一个文件夹。
当用 GetFolder 的时候,你必须:路径既可以写成本地路径也可以写成 UNC 路径(例如:\\accounting\receivables)。但是通配符不能出现在路径当中。此外,你不可以创建一个单独的对象 reference
来同时绑定到多个文件夹。例如,如下的脚本代码会产生一个错误:
objFSO.GetFolder("C:\FSO", "C:\Scripts")
如果你想针对多个文件夹同时工作,你要么用 WMI 或者用针对每个文件夹创建单独的对象reference。
在设置的时候 Set 这个关键词是必须的,因为你要标识这个对象 reference 变量。
例如下面的脚本绑定文件夹 C:\FSO
1 Set objFSO = CreateObject("Scripting.FileSystemObject")
2 Set objFolder = objFSO.GetFolder("C:\FSO")
虽然通配符是不允许的,但是你可以用点来关联到当前的文件夹(.),你可以用点点(..)来绑定到父文件夹,用 \
绑定到根文件夹。例如如下代码语句绑定到当前的文件夹:
Set objFolder = objFSO.GetFolder(".")
※检验文件夹是否存在
许多的文件夹的操作包括复制、移动、删除需要特定的文件夹存在才可以对其进行操作。毕竟,脚本不能复制、移动、删除这些不存在的文件夹,如果一个脚本尝试去对一个不存在的文件夹执行类似的操作,那么会报错“path not found”。为了避免这样的问题发生,你可以用 FolderExists 方法来在你绑定一个文件夹之前检查文件夹是否存在。FolderExists用一个简单的参数(文件夹路径)来返回一个 Boolean 值,如果文件夹存在,返回的值为 True,否则返回的值为 False。
例如,下例中列出的脚本,FolderExists 方法来检查文件夹 C:\FSO 是否存在。如果存在,脚本用 GetFolder 方法来绑定文件夹。如果不存在,脚本就弹出一个 message box 说:Folder does not exist.
1 Set objFSO = CreateObject("Scripting.FileSystemObject")
2 If objFSO.FolderExists("C:\FSO") Then
3 Set objFolder = objFSO.GetFolder("C:\FSO")
4 Wscript.Echo "Folder binding complete."
5 Else
6 Wscript.Echo "Folder does not exist?"
7 End If
※如何建立文件夹
你不可能永远都使用你现有的文件系统结构,从来不去更改你的文件系统结构。而事实上,文件系统是动态的,因为需要变动,现存的文件夹可能被删除,新的文件夹可能被创建。例如,你的单位在
file servers 上给用户提供空间,你需要在每个新用户来的时候创建一个新的文件夹给他。
FSO给脚本编写者提供了这样的能力,可以让你通过脚本来创建文件夹。例如在上例中脚本检查特定的文件夹是否存在,如果存在,脚本用 GetFolder 的的方法来绑定到这个特定的文件夹,如果不存在,脚本弹出一个消息框。虽然这个方法避免了脚本的失败,但是你或许更希望在文件夹不存在的时候来创建一个文件夹,而不是简单的报告说文件夹不存在。为了做到这点,首先创建一个 FSO 对象实例,然后用CreateFolder 方法,将文件夹的完成路径作为参数,例如,下面脚本用这个方法来创建一个新的文件夹 C:\FSO
1 Set objFSO = CreateObject("Scripting.FileSystemObject")
2 Set objFolder = objFSO.CreateFolder("C:\FSO")
如果文件夹存在,那么会报错说”file exists”。基于此,你需要在创建文件夹的之前检查文件对象是否存在。
Note:FSO 只能在本地创建文件夹。如果你想在远程的计算机上创建文件夹,你需要用到 wshcontroller 对象。你可以在本地创建文件夹然后用 WMI 来将文件夹移动到远程的计算 机上。(这个文件夹只能先创建再用WMI 移动,因为 WMI 不支持创建文件夹的方法。)
※ 删除文件夹
当你要删除文件夹时,例如你用文件服务器存储每一个员工的数据,当这个员工离开单位时,他的文件夹将被删除。这保证了文件服务器上的磁盘空间,同样,你或许用一个脚本来创建一个文件夹来存储临时文件,当脚本执行完毕的时候,你会删除这个临时的文件。DeleteFolder方法使你可以删除文件夹和文件夹下的所有文件。DeleteFolder需要一个单独的参数:文件夹的完全路径。例如下面脚本用来删除C:\FSO 和其它所有的东西。
1 Set objFSO = CreateObject("Scripting.FileSystemObject")
2 objFSO.DeleteFolder("C:\FSO")
这个方法会立即删除所有的文件,它不会让你确认是否需要删除就将其扔到垃圾筒。
※使用通配符(Wildcards)删除文件夹
脚本的最大的一个优点就是作为一个管理工具,脚本操作可以同时删除许多个项目。例如你可以用脚本的一个单独的操作就可以删除所有的文件和文件夹,而不用一个一个去删除了。
FSO 允许你用通配符来删除一些特定的文件。例如你希望删除某文件夹下所有以 s 开头的文件夹,这可以用下面的命令来完成。当用类似这样的文件夹结构来运行这个的时候,那么文件夹subfolder1,subfolder2,scripts都会被删除。
objFSO.DeleteFolder("C:\FSO\S*")
下面这个脚本用来删除以 su 开头的文件夹,就是说 subfolder1,subfolder2 会被删除。
objFSO.DeleteFolder("C:\FSO\Su*")
通配符只能放在文件路径的最末尾。
例如如下代码将通配符放在路径的中间,会报错:path not found
objFSO.DeleteFolder("C:\*\Subfolder1")
※复制文件夹及文件夹下的内容
复制文件夹和文件夹当中所有数据能力的对于系统管理任务来说非常重要。有时你需要复制文件夹来创建备份,在计算机 A 和计算机 B 上有同样的文件夹,如果计算机 B 意外的down掉之后,你就不怕数据丢失了。在其它的时候,你或许希望将含有特定文件的文件夹复制到很多台计算机上。用脚本来向各个计算机复制这个文件夹比手工复制高效的多。
1、CopyFolder 方法允许你复制一个文件夹到另外一个地方。当不用通配符的时候,这个CopyFolder 的方法和命令 Xcopy /E 命令一样:它拷贝所有的文件和子文件夹,包括空的文件夹。这个命令需要两个参数:源文件夹->这个文件夹可以被确定为本地路径(C:\Script)或者为 UNC 路径(\\helpdesk\scripts)。目标文件夹->这个文件夹也可以是本地文件夹和 UNC路径。如果目标文件夹不存在,那么将自动创建一个文件夹。此外这个方法有个可选的参数:Overwrite,当这个参数存在时,默认的情况下脚本会覆盖已经存在的目标文件夹。
Note:CopyFolder 方法会在遇到错误时停止,即使这个脚本中包含有 On Error Resume Next语句。例如你用脚本用来拷贝 100 的子文件夹,当成功拷贝三个之后,遇到一个错误。这时脚本会中止,它不去尝试剩下的 97 的文件夹。
在下例中用 CopyFolder 的方法来复制文件夹 C:\scripts 到 c:\FSO 并且复写目标文件夹。注意,这个的结果不是 C:\FSO\Scripts 而是 C:\FSO 下有和 C:\Scripts 有同样的文件。如果你想更改文件夹叫 C:\FSO\Scripts,你应该将目标文件夹改为 C:\FSO\Scripts
1 Const OverWriteFiles = True
2 Set objFSO = CreateObject("Scripting.FileSystemObject")
3 objFSO.CopyFolder "C:\Scripts" , "C:\FSO" , OverWriteFiles
Note:因为这个 CopyFolder 是一个单独的操作,你没有办法来跟踪它的进程。你只是需要简单的等待操作的结束。如果你想监视 copy
命令的过程,你应该用 shell 应用程序。
※使用通配符(Wildcards)复制文件夹
用这个CopyFolder 命令也可以拷贝所有文件夹和子文件夹下的文件。这样可能导致一个问题,如果只是想拷贝 C:\FSO
下的文件,而不是想拷贝C:\FSO\Subfolder1,C:\FSO\Subfolder2,C:\FSO\Sbufolder3 下的文件。
可是,不幸的是这里没有直接的办法来不拷贝子文件夹中的文件而只是拷贝父文件夹中的文件,你可以用通配符来限制子文件夹的拷贝。例如如下的脚本代码只是拷贝以 log
字母开头的文件夹。但是,你用通配符的时候就只是拷贝那些符合特定要求的文件夹才被拷贝:
objFSO.CopyFolder "C:\Scripts\Log*" , "C:\Archive", True
当上面的脚本代码执行的时候,C:\Scripts\logs,C:\Scripts\Logfiles 被拷贝了,包括它们的文件和子文件夹中的文件。但是,在
C:\Scripts 的文件没有被拷贝。当你用 CopyFolder
方法的时候,你不能只是拷贝文件夹中的文件,而不拷贝子文件夹中的蚊子。如果你想只是拷贝文件而不拷贝子文件夹的文件,你可以应用 CopyFile 方法。
※移动文件夹
当你复制一个文件夹从一个地方到另外一个地方的时候,你以复制结束而告终。有时候这个正是你想要的,但是有的时候,你不需要两份文件的信息,你或许希望将文件夹从计算机
A 移动到计算机 B,或者从硬盘 C 移动到硬盘
D。移动工作总会移动到特定磁盘的剩余空间上。例如你或许会周期的将很少访问的文件夹移动到存档磁盘中。此外你或许有一个脚本来监视本地计算机的信息,当监视结束的时候,你需要将它上传到服务器,然后将本地文件删除,然后本地计算机就开始下一次的监视。
MoveFolder 需要两个参数:
1、Source folder 源文件夹,可以是本地路径或者是 UNC 路径。
2、Destination folder 目标文件夹,这个文件系也可是本地或者 UNC 路径。
如果你的目标文件夹不存在,那么移动是成功的。如果目标文件夹存在,那么不成功。你不能用这个 MoveFolder 的方法来覆盖一个文件夹。下例脚本中,移动本地的文件夹 C:\Scripts 到一个共享的文件夹 \\helpdesk\management
1 Set objFSO = CreateObject("Scripting.FileSystemObject")
2 objFSO.MoveFolder "C:\Scripts" , "\\helpdesk\management"
注意用 MoveFolder
方法当脚本失败后不能循环操作。例如,假如你的网络连接在脚本将文件从一个计算机移动到另外一个计算机完成前失败了,那么很有可能你一些文件在计算机 A 上,另外一些文件在计算机 B 上,也有可能在移动的过程中丢失一些文件。因为这个 MoveFolder 的方法无法让脚本恢复到以前状态。
因为如此,你或许需要两个方法来完成文件在网络中的移动:CopyFolder 和 DeleteFolder。你可以用 CopyFolder 来将文件从计算机 A 移动到计算机 B。当复制的操作结束之后,你用DeleteFolder 来删除计算机 A 上的文件,当复制操作失败的时候,你可以确保你的文件还在你的计算机上。
※重命名文件夹
在FSO中没有提供RenameFolder 方法重新命名文件夹。但是可以用
MoveFolder方法来将文件夹移动到相对同样的位置,例如假如有文件夹路径如下:
C:\Scripts\PerformanceMonitoring\Servers\Domain Controllers\Current Logs
如果你用 Windows 浏览器来更改文件夹的名字,那么文件夹还是它本身
C:\Scripts\PerformanceMonitoring\Servers\Domain Controllers\Archived Logs
MoveFolder 的方法可以让你完成同样任务,你将
C:\Scripts\PerformanceMonitoring\Servers\Domain Controllers\Current Logs作为源路径,将 C:\Scripts\PerformanceMonitoring\Servers\Domain Controllers\Archived Logs作为目标路径。这样的结果和在 windows 浏览器中更改文件夹的名字效果是样的。
例如,下例中的脚本将文件夹C:\FSO\Samples更改为C:\FSO\Scripts在脚本运行之前,Sample 是C:\Scripts的文件夹,在脚本运行之后,就是C:\FSO的子文件夹了。此外Scripts (FSO 下的)含有原来 Sample
所有的文件和子文件。
1 Set objFSO = CreateObject("Scripting.FileSystemObject")
2 objFSO.MoveFolder "C:\FSO\Samples" , "C:\FSO\Scripts"
※获得文件夹属性
因为文件夹是一个 COM 对象,它们的属性可以被获得和列举出来,为了列举详细的文件夹信息,你可以用文件夹对象,它是 FSO
的组件之一。文件夹对象的属性列举在了下表中。
Attributes 关于文件夹的属性信息。
DateCreated 文件夹创建的日期
DateLastAccessed 用户最后访问这个文件夹的日期
DateLastModified 用户最后更改文件夹属性的日期
Drive 磁盘信息包括拖尾符号(比如,C:)标识文件夹所在的磁盘
Files 存储在这个文件夹中所有文件的信息。
IsRootFolder Boolean 值来表明这个文件夹是否是根文件夹
Name 文件夹的名字但不包括路径信息
ParentFolder 文件夹存放的父文件夹的名字
Property Description
Path 文件夹的路径
ShortName MS-DOS 类型的文件夹的名字,遵循 8.3 的命名规则。
ShortPath MS-DOS 类型的文件夹的名字,遵循 8.3 的命名规则。
Size 文件夹的大小,单位是 bytes,包含子文件和子文件夹的大小在内。
SubFolders 第一层的子文件夹。在 Subfolders 中的子文件夹不在此列中。
Type 对于文件夹的描述
※列举文件夹的属性
为了得到一个文件夹的属性,一个脚本必须:
1.创建一个 FSO 实例
2.用 GetFolder 的方法绑定特定文件夹
3.用 Echo 的方法显示属性。
当你对一个文件夹的属性进行操作的时候,注意文件的属性和子文件夹的属性是以集合的形式返回的,不是单独返回一个的。此外Attributes 属性返回一个属性值。
1 Set objFSO = CreateObject("Scripting.FileSystemObject")
2 Set objFolder = objFSO.GetFolder("C:\FSO")
3 Wscript.Echo "Date created: " & objFolder.DateCreated
4 Wscript.Echo "Date last accessed: " & objFolder.DateLastAccessed
5 Wscript.Echo "Date last modified: " & objFolder.DateLastModified
6 Wscript.Echo "Drive: " & objFolder.Drive
7 Wscript.Echo "Is root folder: " & objFolder.IsRootFolder
8 Wscript.Echo "Name: " & objFolder.Name
9 Wscript.Echo "Parent folder: " & objFolder.ParentFolder
10 Wscript.Echo "Path: " & objFolder.Path
11 Wscript.Echo "Short name: " & objFolder.ShortName
12 Wscript.Echo "Short path: " & objFolder.ShortPath
13 Wscript.Echo "Size: " & objFolder.Size
14 Wscript.Echo "Type: " & objFolder.Type
※管理文件夹的属性
文件系统支持属性的定义。就是文件夹中和文件夹名字和文件夹大小无关的信息。例如如果你在 Windows 浏览器中右击某个数据夹的时候,点 Properties 你就可以访问文件夹的属性了。
FSO 可以返回几个关于文件夹信息的重要属性。这些属性和相应的值,列举在下表中:
Hidden 2 隐藏,就是在 Windows 浏览器中不可见
System 4 表示它是一个系统文件夹。建议不要更改系统文件夹的内容。
Directory 16 应用到所有的文件夹的标准值。所有可以被 FSO 访问的文件
夹读有这个值,最小位 16。
Archive 32 存盘的属性用在备份程序中,用来决定文件是否需要备份。
如果这个值 enabling 表示文件夹在下一次增量备份中会备份。如果
是disabling 表示不在下一次增量备份中备份
Compressed 2048 表明这个文件夹是否是压缩的
虽然这些看起来很简单,但是从
FSO返回的数据看起来会让你感到很迷惑。例如,你返回一个文件夹的属性值,你或许会看到20,但是这个值并不是像上面一样的标准值。此外,你可能会获得一个单独的值,虽然这个文件夹有说有的属性。这种情况是,脚本并不是返回如
2,4,16,32,2048 这样的值,而只是返回一个 2102。这是因为这些值是以位图的形式显示的。
Note:对于属性值,它返回和存储的数据是以位图的形式的。对于位图你应该不会奇怪,比如后缀名为.bmp 的图形文件
位图看起来就是一系列的开关。如果特定的开关是关闭的,那么这个开关的值就是0,如果是开启的,比如文件夹对象吧,它就具有上表中所列出的值。而位图的值就是这些开关的值的总和。如果你用脚本查询这个文件夹的属性,脚本就返回一个值为 16.
与其相反,如果有三个开关是开启的:隐藏(值是2),Directory(值是16),压缩(值是 2048).那么这个位图的值应该是 2+16+2048,或者值是 2066。这个也是用脚本查询的时候返回的值。位图被设计成只是有一种办法来获取特定的值。得到值 2066 的方法只有一个就是压缩和隐藏。你不可能通过别的数学方法来算得这样的值。
这个设计可以返回值并且决定哪个开关没有开哪个没有开。这个可以允许你来判断文件夹的哪个属性设定了。如果你得到了这个 2066
的值,那么它的属性就是隐藏压缩的文件夹。幸运的是,你不必去作任何单独的数字运算,你可以用逻辑与 AND 操作来决定哪个开关是否是on 还是 off。例如如下代码查询文件夹是否是隐藏的,如果是回显一个信息框: Hidden folder
If objFolder.Attributes AND 2 Then
Wscript.Echo "Hidden folder."
End If
或者: If objFolder.Attributes AND 16 Then
下列脚本先绑定文件夹 C:\FSO,然后回显文件夹属性:
(译者注:这个其实是挨个匹配的,所以你要是测试了两个属性,它都会显示出来的。)
1 Set objFSO = CreateObject("Scripting.FileSystemObject")
2 Set objFolder = objFSO.GetFolder("C:\FSO")
3 If objFolder.Attributes AND 2 Then
4 Wscript.Echo "Hidden folder."
5 End If
6 If objFolder.Attributes AND 4 Then
7 Wscript.Echo "System folder."
8 End If
9 If objFolder.Attributes AND 16 Then
10 Wscript.Echo "Folder."
11 End If
12 If objFolder.Attributes AND 32 Then
13 Wscript.Echo "Archive bit set."
14 End If
15 If objFolder.Attributes AND 2048 Then
16 Wscript.Echo "Compressed folder."
17 End If
※改变文件夹属性
文件夹的每个属性可以看成一个个开关,如果关于隐藏的开关是ON 的,那么这个文件夹就是隐藏的,如果这个开关是 OFF
的,那么这个文件夹就不是隐藏的,你可以选择打开或者关掉它们,对于文件夹的属性也是如此:对于其它的开关,你可以将属性设置为开或者设置为关,你可以用脚本来控制这些开关的,最简单的控制属性开关的办法是用如下的程序:
1. 用 GetFolder 的方法来绑定到一个文件夹。
2. 检查你需要更改的值。例如你想隐藏一个文件夹,看这个文件夹是否是已经隐藏了
3. 如果这个文件夹是隐藏的,用 XOR 操作来将开关转换成非隐藏的。如果这个文件夹不是隐藏的,注意不要用
XOR,如果你用了,开关会被触发,文件夹会中止于隐藏。例如,下列脚本用 AND 来判断文件夹 C:\FSO 的隐藏属性开关是否开着。如果它是开的,脚本用 XOR
操作来将开关关闭来显示文件夹。
1 Set objFSO = CreateObject("Scripting.FileSystemObject")
2 Set objFolder = objFSO.GetFolder("C:\FSO")
3 If objFolder.Attributes AND 2 Then
4 objFolder.Attributes = objFolder.Attributes XOR 2
5 End If
除非很少的一些情况以外,文件夹用来作为单独存放文件的地方。有时候,这些文件夹是系统需要的。例如操作系统希望在特定的文件夹内找到特定的文件。在其它的一些时候,文件夹是系统管理员管理计算机的一种工具也是用户管理文件的工具。系统管理员可能希望将脚本放在叫做Scripts
的文件夹,将一些 trouble-shooting 的工具放在叫做 Diagnostic Tools 的文件夹。用户或许将他们预算信息放在叫做 Budgets
的文件夹,而将薪水的信息放在叫做 Timecards
的文件夹。当然,文件夹一般是限制使用的,你必须知道这个文件夹是存放的是什么文件。系统管理员需要知道是不是有个特定的脚本存在 C:\Scripts 文件夹中,用户需要知道是不是有个特定的窗体存在C:\Budgets 的文件夹中。文件夹对象包含了一些属性可用来返回存储在文件夹内文件集合的信息,为了得到这些信息,你的脚本必须:
1. 创建一个 FSO 对象实例。
2. 用 GetFolder 的方法来绑定到一个适当的文件夹。
3. 设置一个对象 reference 来指向文件夹的属性
4. 用 For Each 循环来列举出所有的文件和他们的属性。脚本不必去绑定到每一个单独的文件去获得文件的信息。
在下列脚本中返回在文件夹 C:\FSO 下所有文件的信息并且回显他们的名字和大小:
1 Set objFSO = CreateObject("Scripting.FileSystemObject")
2 Set objFolder = objFSO.GetFolder("C:\FSO")
3 Set colFiles = objFolder.Files
4 For Each objFile in colFiles
5 Wscript.Echo objFile.Name, objFile.Size
6 Next
对于大多数返回的集合来说,你不能控制输出信息的顺序。就是说你不能将输出的格式定义为按照名字,按照大小或者其它别的原则。如果你想让输出的东西按照特定的格式,你就必须将他们拷贝到数组,目录或者一个 disconnected recordset 然后将他们分类排列。
VBS脚本运行库手册之三——子文件夹
(为了需要知道文件夹中存放的文件,你需要知道文件夹子文件夹,这样,你才能更好的了解文件夹的整体结构。文件夹对象包含了一个 Subfolders 属性,用来放会在文件夹下的 top-level 子文件夹的信息。
Top-level 子文件夹是直接在文件夹下面的。在子文件夹中的子文件夹不在这个范围之内。例如,在下例中,只有 Subfolder1 Subfolder2
是文件夹 scripts 的 top-level 的子文件夹。所以,如果用 Subfolders 这个属性来返回值的话,就只是会返回 Subfolder1 和Subfolder2
为了得到一个子文件夹的集合,你的脚本必须:
1. 创建一个 FSO 实例
2. 用 GetFolder 的方法绑定到一个文件夹。
3. 创建一个对象 reference 来表示 Subfolders 属性。这个是一个集合对象,因为它含有很多个对象。
在得到了对象 reference 这样一个集合之后,你可以用一个 For Each 循环来列举出子文件夹。下列脚本绑定到文件夹
C:\FSO,然后返回子文件夹的大小和名字。除了文件夹的名字之外,你可以返回信息。
1 Set objFSO = CreateObject("Scripting.FileSystemObject")
2 Set objFolder = objFSO.GetFolder("C:\FSO")
3 Set colSubfolders = objFolder.Subfolders
4 For Each objSubfolder in colSubfolders
5 Wscript.Echo objSubfolder.Name, objSubfolder.Size
6 Next
和你的文件系统的设计有关,有时知道 top-level
的子文件夹就可以提供足够的文件夹结构的信息。在多数文件系统中,文件夹是嵌套的,或者是被嵌套的。关于子文件夹的集合可以告诉你C:\Accounting 包含两个子文件夹 2001、2002,但是它不能告诉你他们的子文件夹的信息。
幸运的是,你可以用递归来查询一系列子文件夹的的信息。例如用 Subfolders 属性返回了一些 top-level 的子文件夹:
为返回完整的一系列子文件夹的信息,你需要用递归函数。这个函数用来调用它自己。下面脚本可以列举出文件夹的所有子文件夹:
1. 创建一个 FSO 对象实例
2. 用 GetFolder 的方法来绑定到文件夹 C:\Scripts。GetFolder 的方法用来返回文件夹对象 C:\Scripts.然后这个
C:\Scripts 作为递归子函数 ShowSubFolders 的参数。这个子程序会列举出所有的 C:\Scripts 下的子文件夹。返回一个包含在
C:\Scripts 下所有子文件夹的信息。这个集合有两个值:Subfolder1 和Subfolder 2.
4. 返回第一个参数的路径 Subfolder1。这个子程序用这个来作为参数将它输入给它自身。换句话说,脚本现在调用 ShowSubFolders,并且参数是 Subfolder1。
5. 返回在 Subfolder1 中的所有参数。这个集合有两个值:Subfolder1A and Subfolder 1B.
6. 回显第一个集合的路径,Subfolder1A。子程序用这个来作为参数调用自己。换句话说,就是用 ShowSubFolders 这个函数,那么它的参数是 Subfolder1A 来作参数。
7. 继续执行下一个参数。因为在 Subfolder1A 中没有了子文件夹。这个子程序用Subfolder1B 作为参数。
8. .完成了关于文件夹 Subfolder1 的循环。这个过程结束在 Subfolder1B 没有子文件夹。脚本然后用 Subfolder2
来作为参数,并且重复上面整个过程。
1 Set FSO = CreateObject("Scripting.FileSystemObject")
2 ShowSubfolders FSO.GetFolder("C:\Scripts")
3 Sub ShowSubFolders(Folder)
4 For Each Subfolder in Folder.SubFolders
5 Wscript.Echo Subfolder.Path
6 ShowSubFolders Subfolder
7 Next
8 End Sub
当用 CScript 运行的时候,命令行中显示如下:
C:\scripts\Subfolder 1
C:\scripts\Subfolder 1\Subfolder 1A
C:\scripts\Subfolder 1\Subfolder 1B
C:\scripts\Subfolder 2
C:\scripts\Subfolder 2\Subfolder 2A
C:\scripts\Subfolder 2\Subfolder 2A\Subfolder 2A-1
C:\scripts\Subfolder 2\Subfolder 2B
C:\scripts\Subfolder 2\Subfolder 2C
为了获得完整的文件夹列表,你可以从根文件夹查起:比如 C:\
VBS脚本运行库手册之四——管理文件
(管理文件系统的最终任务还是归结为管理单独的文件。作为一个系统管理员,你的工作是跟踪存储在计算机上的文件。例如,你需要知道正确的诊断工具是否被复制到了 server 上,你需要知道特定的文件(比如游戏文件或者音乐文件)是否被复制到了文件服务器上了,尽管你用了组策略去阻止他们将文件拷贝到文件的server。你需要知道是否有的文件放在计算机上已经有几个月没有去访问了,而这些功能通过类似知道磁盘的剩余空间这样的操作是无法知道的。此外为了跟踪这些文件,你也需要动态的管理他们:需要复制,需要移动,需要重新命名,需要删除。FSO 提供了一些方法来帮助你完成这些管理任务。
Binding to a File
FSO 给你提供了一些方法,比如 CopyFile,DeleteFile
允许你的脚本不用绑定一个特定的文件就可以对文件实例进行操作。而其它的任务,就需要文件对象。比如,为了获得一些属性,你的脚本首先就需要绑定到一个文件,然后再获得文件的属性。GetFile方法允许你绑定到一个独立的文件。为了做到这点,你先创建一个 FSO 实例,然后创建一个文件对象。当你用 GetFile 方法的时候,你必须:1、确定文件的路径:这个路径可以是一个本地路径,也可以是一个 UNC 路径(例如:\\accounting\receivables\scriptlog.txt)在路径当中你不能用通配符也不能有多个文件。用 GetFile 方法你在同一个时刻只能绑定一个单独的文件。2、当绑定的对象分配给一个变量的时候就要用到 Set 这个关键词。
例如,下面脚本绑定到了文件:C:\FSO\Scriptlog.txt
1 Set objFSO = CreateObject("Scripting.FileSystemObject")
2 objFSO.GetFile("C:\FSO\ScriptLog.txt")
一般来讲,你最好用绝对路径来作 GetFile 的参数。这样确保脚本总是可以在文件中运行。但是,用相对路径也是可以的。例如,如下的脚本代码会绑定到
Scriptlog.txt 会成功,因为脚本和这个文件在同一个文件夹里面:
objFSO.GetFile("ScriptLog.txt")
同样,下面的代码实例如果这个脚本和这个文件在同一个文件夹也会绑定到 Scriptlog.txt:
objFSO.GetFile(".\ScriptLog.txt")
注意:FSO 不会用路径环境信息去搜索文件。例如,你可以在命令行中敲如 calc.exe 来打开计算机,而无论你当前的目录是在哪里,因为操作系统默认的情况下去搜索所有的文件的。但是用这个GetFile方法的时候,它不是这样的工作的。
如下脚本除非是在文件夹C:\Windows\System32 的文件夹下,否则都不会运行成功的。
objFSO.GetFile("calc.exe")
有时知道文件是否存在是十分重要的,这个可以作为软件清单的一部分。例如你或许希望检查所有的邮件服务器来看特定的脚本是否存在。用脚本来实现系统管理任务来说,知道脚本存在与否很重要的。比如你或许希望当你复制、移动、删除或者操作不存在的文件的时候,脚本报错。为了避免出现错误,你可以用 FileExists方法来检查文件是否存在。FileExists 方法要求一个单独的参数:文件的路径,然后返回一个特定的 Boolean 值:如果文件存在,返回的是 True,如果文件不存在,返回的是 False。
如下面脚本用 FileExists 方法来检查文件 C:\FSO\Scriptlog.txt 是否存在,如果存在,用 GetFile 方法来绑定到这个文件,如果不存在,脚本回显一个 message“file does not exist”
1 Set objFSO = CreateObject("Scripting.FileSystemObject")
2 If objFSO.FileExists("C:\FSO\ScriptLog.txt") Then
3 Set objFile = objFSO.GetFile("C:\FSO\ScriptLog.txt")
4 Else
5 Wscript.Echo "File does not exist.?
6 End If
你不能用通配符来检查文件夹中特定类型的文件是否存在,你也不能用通配符拉来检查文件夹中所有的文件。如下面脚本,无论在文件夹
C:\FSO\下存在多少文件,都不会报错出来,而是总是返回一个 False.
WScript.Echo objFSO.FileExists("C:\FSO\*.*")
如果你希望遵循一些条件原则检查一些文件而不是基于路径,你有下面两个选择:
1、用 GetFolder
对象来绑定一个文件夹,然后返回文件的属性,这样重复操作直到筛选出你感兴趣的。例如,你可以列举出所有的文件和文件的扩展名,然后跟踪看有多少个后缀名
为.doc 的文件。
2、用 WMI,它允许你创建更多的目标查询。比如选出所有后缀名为.doc 的文件。你可以用计数的办法来返回你要的特定的条目。如果数值是 0,那么,最少有一个是.doc 的文件。
Deleting a File
用 FSO 来创建脚本删除文件让你可以自动执行类似与磁盘清理之类的操作。例如,你可以用脚本定期查询和删除所有的临时文件。你也可以依照一些规则来删除文件,比如删除六个月都没有被访问的文件,或者特定的后缀名的文件,比如(.bmp 或者.mp3)你可以首先创建 FSO 对象实例,然后调用 DeleteFile 方法来删除文件,输入文件路径作为参数。
如下面脚本删除了文件 C:\FSO\Scriptlog.txt
1 Set objFSO = CreateObject("Scripting.FileSystemObject")
2 objFSO.DeleteFile("C:\FSO\ScriptLog.txt")
默认的情况下,DeleteFile 方法不会删除只读文件。事实上,当你尝试删除一个只读文件时,脚本会报错出来。为了避免这样的错误出现,如想删除只读文件的话,需添加强制参数,当强制参数设成 True
的时候,那么 DeleteFile 可以删除任何文件。如下面这行代码可以删除文件 Scriptlog.txt,即使它只是一个只读文件。
1、 objFSO.DeleteFile("C:\FSO\ScriptLog.txt", True)
2、 Deleting a Set of Files
有时候你希望删除特定的、单独的文件。更有可能的是你可能想用脚本来删除一系列的文件。如在周末你或许想删除一些存盘了的日志文件或所有的临时文件。你可以通过通配符来删除一个文件夹之内的一些文件。但是你不能用 DeleteFile 方法来直接删除多个文件夹内部的文件。你可以通过用筛选每个数据夹的办法来逐个删除每个文件夹内部的文件。如果你想删除多个文件夹内部的特定类型的文件(比如删除计算机上全部的.TMP 文件)你应该用 WMI 而不是用 FSO。为了删除一系列文件,调用 DeleteFile 方法,根据文件的名字或者后缀名添加通配符来提供文件夹的路径。例如,下面的代码用来删除文件夹 C:\FSO 文件夹下的所有后缀名为.doc 的文件。
objFSO.DeleteFile("C:\FSO\*.doc")
下面的这行代码用来删除所有文件名带有 log 的文件:
objFSO.DeleteFile("C:\FSO\*log.* ")
正如前面提到的那样,DeleteFile 方法不删除标记为只读的文件。如果你的脚本尝试去删除一个只读的文档,脚本会报错,虽然你用了 On Error Resume Next 语句。例如你想删除 1000 个 txt 文件,并且只有一个文件标记为只读了。当脚本尝试去删除这个文件的时候,会产生一个脚本运行的错误,这个 DeleteFile 方法中止。脚本不去尝试去删除其它的文件,虽然其它的文件不是只读的。因为这个原因,你可以用可选择的:强制参数,将它的值设置成 True。当这个参数设置成 True 的时候,DeleteFile 方法可以删除只读文件。当这个参数被设置成 False 的时候,就不能删除只读文件。
下面脚本用来删除文件夹 C:\FSO 下的所有文件,为了确保删除包括只读文件在内的文件,强制参数被设置成 True。并且使用了这个 DeleteReadOnly 常量。
1 Const DeleteReadOnly = True
2 Set objFSO = CreateObject("Scripting.FileSystemObject")
3 objFSO.DeleteFile("C:\FSO\*.txt"), DeleteReadOnly
如果你想删除所有不是只读的文件怎么办呢?在这种情况下,你可以返回一个集合,用文件属性来获得文件属性,是否为只读,如果是就不删除,否则就删除。
Copying a File
拷贝文件,无论是从计算机上的一个文件夹到另外一个文件夹还是从一个计算机到另外一个计算机,都是一个非常常见的管理任务。如你希望拷贝一个监视的脚本到你所有的服务器上面来取代过去的 DLL 文件。CopyFile方法提供了这种任务管理的办法。CopyFile 方法需要两个必备参数和一个可选的参数:
1、Source - 要拷贝的文件的源文件的路径。
2、Destination — 目标文件夹的路径。为了确保在目标文件夹也是同样的名字,将一个脱尾符放在文件夹的结束之前(“ \ ”)
objFSO.CopyFile "C:\FSO\ScriptLog.txt" , "D:\Archive\"
如果你想重新命名该文件,你可以将全路径写成如下的格式:
objFSO.CopyFile "C:\FSO\ScriptLog.txt" , "D:\Archive\NewFileName.txt" 如果目标文件夹不存在,则会自动创建。
3、Overwrite可选。默认情况下,CopyFile
方法不能复制到目标文件夹存在的同名文件。这导致错误。比如你要更高的版本覆盖存在的文件,那么将这个参数设置成 True。下面脚本拷贝文件 C:\FSO\Scriptlog.txt 到文件夹 D:\Archive。
The original file, C:\FSO\ScriptLog.txt.
The copied file, D:\Archive\ScriptLog.txt.
为了保证拷贝的正常进行,即使存在 D:\Archive\Scriptlog.txt,脚本将可选参数 Overwrite设置成 True(用常量 OverWriteExisting)
1 Const OverwriteExisting = True
2 Set objFSO = CreateObject("Scripting.FileSystemObject")
3 objFSO.CopyFile "C:\FSO\ScriptLog.txt" , "D:\Archive\", overwriteExisting
当你确定一个目标文件夹的时候,脱尾的反斜线是十分必要的。(例如D:\Archive\)如果你的反斜线在,那么文件会被拷贝到 Archive
文件夹,如果反斜线不在的话,脚本的 CopyFile尝试去建立一个叫做 D:\Achive 的文件夹。如果这个文件夹存在,那么会产生一个”Permission denied Error”的错误出来,拷贝的进程会失败的。这个对于只读文件来说也是错误的,虽然你可能将强制参数设置成
True。如果没有反斜线,你必须先将原来的文件删除,然后再调用CopyFile 方法来实现。
Copying a Set of Files
通配符提供了一种拷贝整个文件夹中所有的文件的方法。你可以用脚本复制一些具有共同参数的文件。例如下列脚本拷贝在 C:\FSO 中所有的 txt 文件到 D:\Archive.
1 Const OverwriteExisting = True
2 Set objFSO = CreateObject("Scripting.FileSystemObject")
3 objFSO.CopyFile "C:\FSO\*.txt" , "D:\Archive\" , OverwriteExisting
在 CopyFile 的方法中添加通配符来拷贝文件的时候,只是拷贝当前文件夹的文件。而用CopyFolder
的方法,正好相反,它也拷贝子文件夹的信息和文件。如下的脚本代码语句拷贝文件夹 C:\FSO 中所有的文件,而不拷贝子文件夹的信息。
objFSO.CopyFile "C:\FSO\*.*" , "D:\Archive\"
Moving a File
如你的磁盘空间很低了,你或许希望将文件移动到一个新的位置;如果你的计算机换了规则,你或许希望移动特定的诊断工具取代原来的。在其它的一些情况下,你或许不希望存在这个文件的副本,而是将它移动到一个新的位置。MoveFile
方法让你将文件从一个地方移动到另外一个地方。MoveFile 方法和 CopyFile 的方法类似,你需要创建一个 FSO 对象,然后调用 MoveFile
的方法,需要两个参数:
1、需要移动文件的完整路径
2、需要移动到的目标文件夹的路径,包括脱尾符(反斜线)
下例中脚本将 C:\FSO\Scriptlog.log 移动到 D 盘的 Archive 文件夹
1 Set objFSO = CreateObject("Scripting.FileSystemObject")
2 objFSO.MoveFile "C:\FSO\ScriptLog.log" , "D:\Archive\"
Moving a Set of Files
你也可以用通配符来用一个操作移动多个文件。如为移动所有在文件夹 FSO 中以 data开头的文件,你可以用以下的参数: C:\FSO\Data*.*
通配符可以用来移动特定的文件。因为文件的类型一般是用后缀名来标识的。如下例中脚本移动所有的日志文件到 D:\Archive
1 Set objFSO = CreateObject("Scripting.FileSystemObject")
2 objFSO.MoveFile "C:\FSO\*.log" , "D:\Archive\"
Renaming a File
FSO 并没有提供一种直接的方法来重新命名一个文件。但是和 MoveFolder 的方法可以重新命名文件夹一样,你也可以用 MoveFile
的方法来重新命名文件。为了重新命名一个文件,你可以用 MoveFile 的方法,但是目标文件夹应为当前的文件夹。
如在下例脚本中,将Scriptlog.txt 重新命名为 Backuplog.txt。而在技术上是脚本实际上将 C:\FSO\Scriptlog.txt 移动到一个新的路径 C:\FSO\Backuplog.txt,结果是将文件重新命名了。
1 Set objFSO = CreateObject("Scripting.FileSystemObject")
2 objFSO.MoveFile "C:\FSO\ScriptLog.txt" , "C:\FSO\BackupLog.txt"
文件有些属性对于文件的管理来说十分重要,如属性 DateLastAccssed 告诉你最后打开这个文件的日期。属性Size告诉你文件有多少个 bytes,系统管理员可以通过 windows 资源管理器或者命令行工具去访问文件的属性。虽然这些工具可以返回计算机上文件的信息,但是他们不是被设计成用来保存和操作文件属性的。此外,这些工具大多功能有限,不能使系统管理员很好的通过属性来周期性的清理磁盘或者寻找出满足特定属性的文件集合。
幸运的是,你可以通过 FSO 来获得计算机上任何文件的详细信息。和其它的工具相比,FSO可以让你查询一个文件或者一组文件的信息。
Attributes 文件的位图属性。
DateCreated 文件的创建日期。
DateLastAccessed 数据的最后访问日期。
DateLastModified 数据的最后更改日期。
Drive 磁盘驱动器和托尾符(例如:C:)代表文件存在的磁盘上。
Name 文件的名字,不包含文件的路径
ParentFolder 父文件夹的名字。例如,C:\Windows\System32\Scrrun.dll
的父文件夹名字是 Windows
Path 文件的路径,比如 C:\Windows\System32\Scrrun.dll
ShortName MS-DOS类型的文件名,符合8.3 命名规则的。
ShortPath MS-DOS-style 文件名字。符合 8.3 命名规则的。
Size 文件的大小,单位是 bytes
Type 文件类型,是在注册表区中对于此文件类型的字符串描述。
为了访问这些属性,你必须:
1. 创建一个 FSO 对象实例。
2. 用 GetFile 方法创建一个对象的 reference。脚本必须给 GetFile 提供文件的路径参数。
3. 用 Echo(或者其它的处理办法)来表达出文件的属性。
如下面的脚本用 GetFile 的方法来绑定到文件 C:\Windows\System32\Scrrun.dll 然后返回一些它的属性:
1 Set objFSO = CreateObject("Scripting.FileSystemObject")
2 Set objFile = objFSO.GetFile("c:\windows\system32\scrrun.dll")
3 Wscript.Echo "Date created: " & objFile.DateCreated
4 Wscript.Echo "Date last accessed: " & objFile.DateLastAccessed
5 Wscript.Echo "Date last modified: " & objFile.DateLastModified
6 Wscript.Echo "Drive: " & objFile.Drive
7 Wscript.Echo "Name: " & objFile.Name
8 Wscript.Echo "Parent folder: " & objFile.ParentFolder
9 Wscript.Echo "Path: " & objFile.Path
10 Wscript.Echo "Short name: " & objFile.ShortName
11 Wscript.Echo "Short path: " & objFile.ShortPath
12 Wscript.Echo "Size: " & objFile.Size
13 Wscript.Echo "Type: " & objFile.Type
和文件夹一样,文件有些属性可以通过 FSO 返回和配置,有些属性比如位图属性就不能通过 FSO 来获得的。
Normal 0 没有这样的属性设置为 True。??
Read-only 1 文件为只读。
Hidden 2 文件在 Windows 浏览器中是隐藏的。
System 4 文件是操作系统必须的。
Archive 32 存盘,文件标记为需要备份的。
Alias 64 此文件是其它文件的快捷方式
Compressed 2048 此文件是压缩的。
想获得文件的这些属性,用GetFile 方法来绑定一个文件,在你创建完了这个文件对象的reference
之后,你可以用逻辑与操作来决定这些属性。如果没有任何属性被设置了,那么这个值就是 0。如下面脚本绑定了文件 C:\FSO\Scriptlog.txt
然后检查每个属性并用FSO 返回属性的值。
1 Set objFSO = CreateObject("Scripting.FileSystemObject")
2 Set objFile = objFSO.GetFile("C:\FSO\ScriptLog.txt")
3 If objFile.Attributes AND 0 Then
4 Wscript.Echo "No attributes set."
5 End If
6 If objFile.Attributes AND 1 Then
7 Wscript.Echo "Read-only."
8 End If
9 If objFile.Attributes AND 2 Then
10 Wscript.Echo "Hidden file."
11 End If
12 If objFile.Attributes AND 4 Then
13 Wscript.Echo "System file."
14 End If
15 If objFile.Attributes AND 32 Then
16 Wscript.Echo "Archive bit set."
17 End If
18 If objFile.Attributes AND 64 Then
19 Wscript.Echo "Link or shortcut."
20 End If
21 If objFile.Attributes AND 2048 Then
22 Wscript.Echo "Compressed file."
23 End If
Configuring File Attributes
除了列举文件的属性之外,FSO 提供了配置如下属性的一些办法:
1、ReadOnly 2、Hidden 3、System 4、Archive
为了配置文件的这些属性,脚本可以用如下的进程:
1. 用 GetFile 的方法绑定一个文件。
2. 检查你要更改的文件的属性。如你想将一个文件设置成只读的那么就检查它是否是已经是已读了。
3. 如果不是只读的,那么用逻辑 XOR 操作来触发这个开关。这样可以使文件变成只读的。但 是如果文件已经是只读的,那么不要用逻辑 XOR
操作,如果你用了,那么这个属性就被移掉了。
下面脚本用 AND 操作来检查只读开关是否已经打开,从而确定文件C:\FSO\TestScript.vbs是否为只读。如果为只读,脚本用 XOR
操作来将其改成非只读的。
1 Set objFSO = CreateObject("Scripting.FileSystemObject")
2 Set objFile = objFSO.GetFile("C:\FSO\TestScript.vbs")
3 If objFile.Attributes AND 1 Then
4 objFile.Attributes = objFile.Attributes XOR 1
5 End If
你也用如下的语句来同时去更改系统、存盘、隐藏等属性
objFile.Attributes = objFile.Attributes AND 0
VBS脚本运行库手册之五——文本文件的建立、追加、删除等
(文本文件对于系统管理员来说是一个强大的系统管工具。这个对于现在的高级的图形界面和多用户的操作系统来说好象是不可能的。但是,简单的文本文件,比如 notepad 文件,仍然是系统管理的一个关键元素。文本文件是轻便而且便于维护的。他们占用较少的磁盘空间不需要其它多余的软件支持。文本文件可以简单的工作并且非常容易携带。用文本文件写的脚本文件可以被复制和察看任何计算机的信息,虽然它运行的系统不是 Windows.此外,它还提供了快捷,简单,标准的办法来向脚本输入和输出数据。文本文件可以存储向脚本中输入的数据arguments)或者向脚本中硬编码。这样你就不用向命令行中输入100个服务器的名字,脚本可以从文本文件中读这些信息。同样,文本文件为存储脚本获取的信息提供了快捷简单的方法。这些数据可以直接写到数据库,但是这个要求在服务器上作额外的配置,额外的脚本代码,在脚本运行时候额外的管理。但是数据可以存在文本文件中,然后在稍后导入到数据中去。FSO 提供一些读写文本文件的方法。
Creating Text Files
FSO 让你可以用现存在的文本工作,也可以让你自己创建脚本。为了创建一个新的文本文件,简单的创建一个 FSO 对象,然后调用 CreateTextFile 方法,输入完整的文件路径信息作为这个方法的参数。
例如,如下的脚本代码在文件夹 C:\FSO 中创建了一个 Scriptlog.txt 的文件:
1 Set objFSO = CreateObject("Scripting.FileSystemObject")
2 Set objFile = objFSO.CreateTextFile("C:\FSO\ScriptLog.txt")
如果这个文件存在,那么 CreateTextFile 方法会创建一个。如果这个文件已经存在,那么CreateTextFile 方法会复写存在的文本文件,而以新的空白的文件取代它。如果你希望这个文件不被取代,那么就用一个可以选择 Overwrite 的参数。当这个参数设置成 False 的时候,攒在的文件就不被复写。当这个参数被设置成 True(默认的是 True),那么就会复写存在的文件。例如,如下的脚本如果创建的文件存在的话就不会复写。
Set objFile = objFSO.CreateTextFile("C:\FSO\ScriptLog.txt", False)
如果你将参数设置成False,并且文件存在,那么就会有一个运行的错误。因为如此,你可以检查文件是否存在,如果存在,那么就作其它的操作,例如允许用户更改一个新的文件名。
Creating File Names Within the Script
防止文件存在产生错误的方法是用脚本给文本文件生成一个名字。因为文件名字生成器并不创建一个有意义的名字,这对于你想在未来要命名的日志和其它文件来说不是一个好的办法。但是,这个对于需要临时文件的脚本来说保证了有个特定的文件名。例如,你或许想让你的脚本将数据保存在HTML 或者 XML 格式,然后将这些数据在 WEB 浏览器中显示,然后将这个临时文件在web 浏览器关掉时删除。在这种情况下,你可以用GetTempName 方法来生成一个特有的文件名。
为了生成一个特别的文件名,脚本首先要创建一个 FSO 对象实例然后调用 GetTempName 方法(不用参数。)例如在 4.33 中的脚本用一个 For Next 循环来随机生成 10 文件名字。
1 Set objFSO = CreateObject("Scripting.FileSystemObject")
2 For i = 1 to 10
3 strTempFile = objFSO.GetTempName
4 Wscript.Echo strTempFile
5 Next
用 GetTempName 来生成文件的名字生成的不是唯一的。部分原因是生成名称的算法,部 分是因为可能的名字的数量是有限的。文件名字被限制为 8 个字节,而且前三个字节规定为 rad,例如,你用脚本来创建 10000 个文件名字,在第 9894 个文件名字之后就不再是唯一的了,剩下的 106 个 53 对是复制的。
在 4.34 中的示例脚本用 GetTempName 方法来创建一个文件,脚本必须:
1. 创建一个 FSO 对象实例。
2. 创建一个给文件夹 C:\FSO 的变量叫做 strPath.
3. 用 GetTempName 方法来生成一个单独的文件名子。
4. 用 BuildPath 的方法来合并文件夹名字和文件名字来创建一个完成的临时文件的名字。这个 整个路径存储在 strFullName 变量中。
5. 调用 CreateTextFile 方法,用 strFullName 来作参数。
6. 在创建了这个文件之后,关闭这个文件。在生产环境中,大多数情况下,你可能要向里面写 了数据之后再关闭它。
1 Set objFSO = CreateObject("Scripting.FileSystemObject")
2 strPath = "C:\FSO"
3 strFileName = objFSO.GetTempName
4 strFullName = objFSO.BuildPath(strPath, strFileName)
5 Set objFile = objFSO.CreateTextFile(strFullName)
6 objFile.Close
Opening Text Files
用文本文件来工作有三个主要的步骤。在你可以作其它的事情之前,你必须打开文本文件。这个你可以打开存在的文件或者创建一个新的文本文件,创建结束之后,默认文件是打开的。每个方法都是返回一个TextStream 对象实例。
在你获得了 TextStream 对象之后,你可以向这个文件中写或者读这个文件。但是你不能向同一个文件读和写。换句话说,在同一个操作中,你不能打开一个文件,读这个文件然后再向这个文件中写东西。你必须读完这个文件后关闭,然后再打开这个文件,写入数据,然后关闭。
当你打开一个存在的文件,这个文件要么是准备好被读的,要么是准备好被写的。如果你创建一个新的文本文件,这个文本文件只是被读的,没有什么其它原因的话,它没有内容去被读。最后,你要去关闭这个文本文件,虽然它不是必须的,因为在脚本结束的时候,它会自动关闭,但是这个对于程序实践来说是个好的办法。
为了打开一个文本文件:
1. 创建一个 FSO 对象实例。
2. 用:OpenTextFile 来打开一个文本文件。这个 OpenTextFile 需要两个参数,一个是一个文 件的路径,另外一个是跟着的参数:
For reading (parameter value = 1, constant = ForReading).
文件被打开之后只是被用来为读作准备的,为了写这个文件,你必须再一次的打开文件,用参数 ForWriting 或者 ForAppending。
For writing (parameter value 2, constant = ForWriting).
文件被打开,并且写入的数据覆盖了原来存在的数据,就是说旧的数据被删除,新的被添加。用这个方法用新的数据来代替存在的数据。
For appending (parameter value 8, constant = ForAppending).
文件在这种模式下打开,并且新的数据添加到文件的末尾。用这个方法向存在的文件中添加新的数据。
当打开文件的时候,你必须使用适当的参数。当你用读的模式打开一个文件而试图向里面写东西的时候,你会收到一个 bad file mode
的错误。你如果试图打开一个非文本文件的话也会收到同样的错误的。你可以直接用数值(比如 1 代表写)或者创建一个常量然后赋值给它适当的值。例如,如下的两种方法都可以打开一个文件并且准备被读:
Const ForReading = 1
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("C:\FSO\ScriptLog.txt", ForReading)
Set objFile2 = objFSO.OpenTextFile("C:\FSO\ScriptLog2.txt", 1)
但是在没有定义这个常量的时候,你不能直接用。这是因为事实上 VB 脚本并没有这些 COM 对象常量。如下的脚本会返回一个 invalid procedure call or argument 的错误并且失败。这是因为ForReading 这个常量并没有显式的定义。因为它没有定义,所以这变量默认情况下被赋值为 0,而 0 对于 OpenTextFile 来说并不是一个合法的参数:
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("C:\FSO\ScriptLog.txt", ForReading)
在 4.35 中的脚本打开了 C:\FSO\Scriptlog.txt 准备读,用了用户自己定义的变量,并且赋值为 1.
Listing 4.35 Opening a Text File for Reading
1 Const ForReading = 1
2 Set objFSO = CreateObject("Scripting.FileSystemObject")
3 Set objFile = objFSO.OpenTextFile("C:\FSO\ScriptLog.txt", ForReading)
Closing Text Files
所有的用脚本打开的文本文件在脚本结束的时候会自动关闭。因为如此,比不是必须的去显式的关闭一个文本文件。然而,关闭这个文本文件却是一个很好的程序实践并且在下面的一些情况下,如果不关闭文件的话,会产生一些错误。
Reread the file.
有些时候你或许希望用一个脚本多次读一个文件。你或许打开一个文本文件然后将它所有的内容全部保存给一个字符串变量,然后搜索这个字符串来查找特定的错误的代码。当这个错误找到了,你再逐行去读取文件,提炼出有错误的每一行。如果你尝试多次去读一个文件,你不会收到你期待的结果,而是会遇到一个运行的错误。例如下的脚本读取一个文本文件,返回文件的内容到屏幕,然后尝试重复这样的过程:
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("C:\FSO\ScriptLog.txt", 1)
Wscript.Echo "Reading file the first time:"
strContents = objFile.ReadAll
Wscript.Echo strContents
Wscript.Echo "Reading file the second time:"
Do While objFile.AtEndOfStream = False
strLine = objFile.ReadLine
Wscript.Echo strLine
Loop
当在 cscript 下运行的是命令行的信息如下:
Reading file the first time:
File line 1.
File line 2.
File line 3.
Reading file the second time:
第一次文件是被读取的,内容存储在变量 strContents 上,第二次,文件读取的时候,没什有数据回显在屏幕上,这是因为文件已经到达了文件的末尾,没有其它的数据给你读了。为了读取这个数据,你必须关闭这个文件然后重新打开它。你不能在读到文件的末尾之后去跳到文件的开头去了。TextStreamObject.Close 方法用来关闭一个文本文件。例如,在 4.36 中的脚本创建一个FSO对象实例,然后打开一个文本文件,然后在立即关闭了。为了访问文件的内容,你必须再一次的调用 OpenTextFile 方法去重新打开这个文件。
Listing 4.36 Opening and Closing a Text File
1 Set objFSO = CreateObject("Scripting.FileSystemObject")
2 Set objFile = objFSO.OpenTextFile("C:\FSO\ScriptLog.txt", 1)
3 objFile.Close
Reading Text Files
在许多的企业脚本中,读取文本文件的内容是个标准进程,你可以用这个功能来读取命令行的参数。例如,你的文本文件包含了计算机名字的列表,脚本审计成读取这个列表,然后在每个计算机上运行这个脚本。搜索满足特定条件的日志文件。例如,你或许想找所有有错误的日志。将日志文件中添加内容并且汇入到数据库。例如,你或许有一个服务或者程序来保存信息到特定存文本文件格式。然后你用脚本来读取这个文件拷贝相关的信息到数据库中。
可以用 FSO 来都读取文本文件的内容,但是有以下几点你需要牢记:FSO 只能读取 ASCII 的文本文件。你不能用 FSO 读取unicode 或者
binary 文件格式的文件,比如 microsoft word 或者是 Microsoft excel .用 FSO读取文本文件的时候,只能有一种方式:从前到后。此外,FSO 读取文件的时候是逐行读取的。如果你试图返回前面的行,你必须从开始再重新读取,直到特定的行。
你不能打开一个文件同时用来读和写。如果你打开一个文件是为了读的,那么你想修改文件内容的时候,你必须重新打开文件。如果你尝试在写的模式下读取文件,那么你会收到一个bad file mode 的错误。
还有读取特定的字符然后停止也是我们常用的技巧。例如,如下的命令会读取第一行的前 12 个字符Alerter.Shar,并且将它赋值给变量
strText,然后停止:strText = objTextFile.Read(12)
ReadLine 读取文本文件中每行的信息然后在到达新的一行的之前停止。例如,如下的命令读取第一行并且将它赋值给变量strText,然后停止。strText = objTextFile.ReadLine
为了逐行读取整个文件的内容,将 ReadLine 放在一个循环中。
ReadAll Reads the entire contents of a text file into a variable.
Skip 跳过特定的数目的字符之后停止。例如,如下的命令面跳过前面的 12 字节,后操作都说从第 13 个字符开始。
objTextFile.Skip(12)
SkipLine 跳过一整行。例如,如下的代码先读第一行,然后读取第三行。跳过了第二行
strText = objTextFile.Readline
objTextFile.SkipLine
strText = objTextFile.Readline
虽然在单独的一个字符串中寻找东西是很简单,但是不是要求特别快。用 ReadAll 方法在读取近 6000 行的测试文件的时候,每秒钟搜索大约 388kb。逐行读取的话也会小于一分钟的。
为了用 ReadAll 方法,打开一个文本文文件,然后调用 ReadAll 方法(不用参数的)例如在 4.38的脚本,打开文件C:\FSO\Scriptlog.txt,然后读取文件的内容,蒋数据存储在变量strContents 中,脚本然后回显这个变量的值,就是回显了文本文件的内容。
Listing 4.38 Reading a Text File Using the ReadAll Method
1 Const ForReading = 1
2 Set objFSO = CreateObject("Scripting.FileSystemObject")
3 Set objFile = objFSO.OpenTextFile("C:\FSO\ScriptLog.txt", ForReading)
4 strContents = objFile.ReadAll
5 Wscript.Echo strContents
6 objFile.Close
Reading a Text File Line by Line
为了达到系统管理的目的,文本文件有的时候像一个扁平的上数据库一样的工作。每一行代表数据库的一个纪录。例如,脚本经常在文本文件中读取服务器的名字,然后针对这些服务器来执行操作。在这些情况下,文本文件看起来和下面这些差不多的:
atl-dc-01
atl-dc-02
atl-dc-03
atl-dc-04
当文本文件当做扁平数据库的时候,脚本被用来读取每个纪录然后针对每个纪录作一些工作。例如,一个脚本或许读取第一个计算机名子,连接到它,实现一些功能。然后这个脚本读取第二个计算机的名字,连接到它,然后实现同样的功能。这个进程直到读取完了所有的计算机名字,然后结束。
ReadLine 方法可以让你的脚本读取文本文件中单个的行。为了这个方法,打开一个文本文件,然后用一个 Do Loop 循环直到文件的
AtEndOfStream 属性为真。在这个循环中,调用ReadLine
方法,存储第一行的内容到一个变量,然后作一些动作。当脚本执行循环的时候,热它自动丢弃第一行的内容读取第二行的内容到这个变量。这个过程持续进行直到文件的结束。
例如,在 4.39 中的脚本打开文件 C:\FSO\Serverlist.txt 然后逐行读取整个文件,将内容回显在屏幕上:
Listing 4.39 Reading a Text File Using the ReadLine Method
1 Set objFSO = CreateObject("Scripting.FileSystemObject")
2 Set objFile = objFSO.OpenTextFile("C:\FSO\ServerList.txt", 1)
3 Do Until objFile.AtEndOfStream
4 strLine = objFile.ReadLine
5 Wscript.Echo strLine
6 Loop
7 objFile.Close
"Reading" a Text File from the Bottom to the Top
正如前面提到的,FSO 只能从文本文件的前面读到后面,你不能从后面向前面读。这个对于日志文件来说有一些问题,大部分的日志文件是按照日期格式来写的,它的最开始的第一个日志被纪录在第一行,第二个纪录纪录在第二行,依次类推。这就以为着你感兴趣的事情,最新的日志往往在文件末尾。
有的时候你希望按照反日期的顺序来显示信息,就是最近的纪录最先显示,最旧的纪录最后显示。虽然你不能从后面先前的读取文本文件,但是你要可以实现上面的目的,脚本必须:
1. 创建一个数组来存储文本文件的每行信息
2. 用 ReadLine 的方法读取文本文件的每行信息,然后将每行的信息作为数组的一个独立的元 素存储在数组中。
3. 在屏幕上显示数组的信息,从数组的最后面开始,向前显示。
例如,在 4.40 中的脚本读取文件 C:\FSO\Scriptlog.txt 的信息,然后将每行的信息作为数组
的一个元素存储在数组 arrFileLine 中。在整个文件被读了之后,从数组的最后一个开始回显数组的信息。为了做到这点,用了一个 for
循环,从数组的最后一个元素,the upper bound of array 逐渐增长到第一个元素,the lower bound of array
1 Dim arrFileLines()
2 i = 0
3 Set objFSO = CreateObject("Scripting.FileSystemObject")
4 Set objFile = objFSO.OpenTextFile("C:\FSO\ScriptLog.txt", 1)
5 Do Until objFile.AtEndOfStream
6 Redim Preserve arrFileLines(i)
7 arrFileLines(i) = objFile.ReadLine
8 i = i + 1
9 Loop
10 objFile.Close
11 For l = Ubound(arrFileLines) to LBound(arrFileLines) Step -1
12 Wscript.Echo arrFileLines(l)
13 Next
如果文件的内容和下面的一样:
6/19/2002 Success
6/20/2002 Failure
6/21/2002 Failure
6/22/2002 Failure
6/23/2002 Success
那么在屏幕上回显的信息如下:
6/23/2002 Success
6/22/2002 Failure
6/21/2002 Failure
6/20/2002 Failure
6/19/2002 Success
Reading a Text File Character by Character
在一些定宽的文本文件中,区域是受长度的限制的。第一个 field1 或许包含 15 个字节,第 2个 field 或许含有十个,依次类推。那么这样的文件看起来和下面的差不多:
Server Value Status
atl-dc-0119345 OK
atl-printserver-02 00042 OK
atl-win2kpro-0500000 Failed
在有些情况下,你或许只是想获得 values 的值或者只是想获得 status 的信息。这个 value 的信息,容易表明。Values 总是从第 26
个字符开始,不超过 5 个字符。为了获得这些值,你只需要获得第 26,27,28,29,30 个字符的信息。
方法 Read 允许你读取特定数目的字节。它的一个单独的参数就是你要读的字节的个数。例如,如下的脚本代码示例,读取后面的 7 个字节,并将读取的值存给变量 strCharacters:
strCharacters = objFile.Read(7)
用 Skip 和 SkipLine 方法,你可以获得文本文件中你选择的特定字符。例如,在脚本 4.41 中只是读取每行的第 6 个字节。为了做到这点,脚本必须:
1. 跳过每行前面五个字节用 Skip(5)
2. 用 Read(1)读取第 6 个字节
3. 跳到下面一行。
Listing 4.41 Reading a Fixed-Width Text File
1 Set objFSO = CreateObject("Scripting.FileSystemObject")
2 Set objFile = objFSO.OpenTextFile("C:\FSO\ScriptLog.txt", 1)
3 Do Until objFile.AtEndOfStream
4 objFile.Skip(5)
5 strCharacters = objFile.Read(1)
6 Wscript.Echo strCharacters
7 objFile.SkipLine
8 Loop
为了更好说明脚本是如何工作的,我们假设脚本文件 C:\FSO\Scriptlog.txt 看起来是如下的样子的:
XXXXX1XXXXXXXXXXXXXX
XXXXX2XXXXXXXXXXXXXXXXXXX
XXXXX3XXXXXXXXXXXXx
XXXXX4XXXXXXXXXXXXXXXXXXXXXXXXX
对于这个文件的每行,前面的五个字符都是 x,第六个是数字,后面是随机数量的 x。当脚本4.41 运行的时候,脚本会:
1. 打开文本文件从第一行开始读起。
2. 跳过前面五个字符。
3. 用 Read 载方法读取第六个字符
4. 回显字符在屏幕上
5. 跳到下面一行重复上面的进程,直到脚本运行结束。.
Writing to Text Files
像文本文件中写入数据是写系统管理脚本的另外一个强大的功用。文本文件提供了一种方法可以让你将脚本获得的输入存储在其中。输入可以替代现有的文字或者是添加到现有的文字后面。文本文文件也可以纪录脚本的执行情况。这个对于debug 脚本来说费城有用。将脚本的执行纪录放在一个文本文件中,你可以过阵子来察看这个日志去决定脚本实际执行了哪些而哪些没有执行。FSO 给你提供了向文本文件中写如数据的能力。为了用 FSO 脚本向一个文本文件中写入数据,你必须:
1. 创建一个 FSO 对象实例。
2. 用 OpenTextFile 方法打开这个文本文件,你可以以两种方法打开:
For writing (parameter value 2, constant = ForWriting).
在这个模式下,写入新的数据会覆盖旧的数据。(旧的数据会被删除,新的数据添加上去。)
用这个模式,是将存在的文件换上新的内容。
For appending (parameter value 8, constant = ForAppending).
在这种模式下,数据写在原来文件的末尾。用这种模式向现有的文件中添加数据。
3. 用或者 Write,WriteLine,WriteBlankLine 方法来向文本文件中写入数据。
4. 关闭文本文件
向文本文件中写数据的三种方法在表格 4.9 中:
Table 4.9 Write Methods Available to the FileSystemObject
Method Description
Write 向文本文件中写入数据,不是添加文件到末尾。不会自动换行。
例如,如下代码写入两个单独的字符串:
objFile.Write ("This is line 1.")
objFile.Write ("This is line 2.")
MethodDescription
返回的数据类似如下:
This is line 1.This is line 2.
WriteLine 向文本文件中写入数据后添加一个换行符号,然后区自动换行的。
比如,如下的代码:
objFile.WriteLine ("This is line 1.")
objFile.WriteLine ("This is line 2.")
结果输出的信息如下:
This is line 1.
This is line 2.
WriteBlankLines 向文本文件中写入特定数目空白的行。例如如下的代码向文本文件中写入两行独立的文字,然后用空白的行分开:
objFile.Writeline ("This is line 1.")
objFile.WriteBlankLines(1)
objFile.Writeline ("This is line 2.")
输出的结果如下:
This is line 1.
This is line 2.
除了在表格 4.9 中的方法之外,VB 脚本提供了常量 VbTab 来向文本文件中写入。VbTab
向两个字符中插入一个空格。为了创建一个空格分隔的文件,代码和下面的类似:
objTextFile.WriteLine(objService.DisplayName & vbTab & objService.State)
FSO 的一个弱点就是它不能直接修改特定的行的信息。你不能写类似下面的命令:跳过到第五行,更改一些数据,然后保存成新的文件。为了修改在一个十行文件中的第五行,你的脚本必须
1. .读取整个 10 行
2. .将 1-4 行写回文件。
3. 写入修改好的第五行的内容。
4. 写入第 6 行到第 10 行的内容。
Overwriting Existing Data
在系统管理中,简洁是一种美德。例如,假如你的脚本每天晚上运行,在你的 DC 上从事件日志中获得日志,将这些事件写入数据库中,纪录哪个计算机可以成功的连接到,哪个不可以。基于一些历史的目的,你或许希望跟踪明年一整年的所有成功和失败的纪录。这个对于脚本刚开始生效和网络不稳定的情况下来说,都是非常重要的。从另外的情况来说,你或许只是希望刚才脚本运行的时候发生了什么。换句话说说,你不是希望要一个日志中过去的 365 天的信息,而是要距离最近的信息。它让你可以很快的打开文件并且查找看脚本是否按照计划的运行。
当你的文本文件用 ForWriting 模式打开的,任何写入的新的数据会替代原来存在的文件。例如,假如你的文本文件里面存储了整个莎士比亚的故事全集,但是你用脚本以 ForWriting 模式打开了这个文本,并且向里面写了一个字母 a,那么当你的这个文件写完关闭之后,它就只是包含一个字母 a,原来的数据就全部丢失了。
这个脚本以 ForWriting 模式打开脚本 C:\FSO\Scriptlog.txt 然后将当前的日期和时间写入文件。每当脚本运行的时候,旧的时间和日期被新的取代,这个文本文件就永远只是有单独的一个日期的值。
Listing 4.42 Overwriting Existing Data
1 Const ForWriting = 2
2 Set objFSO = CreateObject("Scripting.FileSystemObject")
3 Set objFile = objFSO.OpenTextFile("C:\FSO\ScriptLog.txt", ForWriting)
4 objFile.Write Now
5 objFile.Close
Appending New Data to Existing Data
许多脚本被设计成为在特定的时间间隔的时候运行,收据数据,保存数据。这些脚本的是用来分析趋势或者在过去的时间内部的使用情况。在这些情况下,你就不希望删除现在存在的数据而去替代成新的数据。
例如,假如你用你的脚本监视进程使用量。在任何一个时间点上,进程的使用量应该是在 0 到100 之间的一个值,对于它自己来说,单个的值没有什么意义。为了获得进程使用的一个完整的图景,你需要重复不断的监视使用情况并纪录它的值。如果你的进程在每几秒钟之内返回的数据是9%,17%,92%,90%,79%,88%,91%那么你的进程使用是非常高的。这个就可以对比出这个时间内的使用情况。
如果以 ForAppending 的模式打开一个文件,你可以使数据不是覆盖现有的数据,它的数据是添加到文件的底部。例如,在 4.43 中的脚本打开一个文本文件,并且将当前的日期和时间写入文件。因为是以 ForAppending
的模式打开的文件,当前的日期会卸载文件的底部。如果你在不同的时候分别运行脚本,你的脚本结束时候大约如下面的信息:
6/25/2002 8:49:47 AM
6/25/2002 8:49:48 AM
6/25/2002 8:50:33 AM
6/25/2002 8:50:35 AM
Listing 4.43 Appending Data to a Text File
1 Const ForAppending = 8
2 Set objFSO = CreateObject("Scripting.FileSystemObject")
3 Set objFile = objFSO.OpenTextFile("C:\FSO\ScriptLog.txt", ForAppending)
4 objFile.WriteLine Now
5 objFile.Close
上面的脚本用 WriteLine 方法写入数据保证每个日期都是独占一行的。如果这个脚本用 Write
的方法来写的话,那么这个脚本运行结束的时候,数据写在一起,如下的样子:
6/25/2002 8:49:47 AM6/25/2002 8:49:48 AM6/25/2002 8:50:33 AM6/25/2002 8:50:35 AM
VBS脚本运行库手册之六——Dictionary对象
(Dictionary Object
脚本经常用来从外面的源获得信息,例如从文本文件或者数据库文件。在这些信息获得之后,它将被存储到它的内存中,然后用脚本来更改它。例如将信息存储在一个变量中,或者存储在一个数组中。同样,这些信息可以存储在Dictionary 对象中。Dictionary 就好象一个数组一样。不同的是,它用叫做键对(key-item pairs)来存储信息,而数组用数字值来存储。例如,一个一维的数组存储州的首府像下面这样:
0 - Olympia
1 - Salem
2 - Sacramento
于其相反,Dictionary 存储信息像如下的格式:
Washington – Olympia
Oregon - Salem
California - Sacramento
如果你的命令行只是要求输入一个参数,比如计算机名字,你可以用数组来完成这样的工作。但是,Dictionary 提供了比数组更加多的功能。特别指出的是,Dictionary 不要求一个脚本确定需要存储的元素的数目。在数组中,你必须确定数组的大小,或者当添加数据的时候重新变更数组的大小。知道每个值对应的 index。在Dictionary 中,值可以通过 key 或者 item 自身来访问。
这样,当管理员用脚本从外部获得信息,比如获得计算机的名字,然后存储在内存中以备使用的时候,Dictionary 就成了一个理想的工具。
Creating a Dictionary
因为 Dictionary 是一个 COM 对象,你必须用其它的 COM 对象那样去初始化它。如下的代码创建一个 Dictionary 对象。
Set objDictionary = CreateObject("Scripting.Dictionary")
当创建完了一个 Dictionary 对象之后,你可以配置 Dictionary 的属性或者向其内部添加元素。
Configuring Dictionary Properties
Dictionary 只有一个配置的属性,CompareMode,它对于要添加哪个 KEY 或者不能添加哪个 KEY起着非常重要的作用。(检查 key 是否存在在 Dictionary 中也是十分重要的)默认的情况下,Dictionary 是被创建成二进制模式,基于 ASCII 的值。知道这个很重要,因为在ASCII 中,大写和小写字符是不同的。在二进制中,如下的两个服务可以同时被添加进来:
alerter
ALERTER
换句话说,在二进制模式下,你可能因为大小写不同而添加进来很多个同样的 item。这样就难于搜索了。如果你想查找 Alerter 这个KEY,你可能得到它不存在,因为没有和它大小写完全一样的值。这样你可能向一个 Dictionary 添加同样的 item。当一个 Dictionary 被配置成text 模式的时候,大小写被认为是一样的,这样可以除去同样的 key,你的 Dictionary 如果存在 alerter 的话,你就不能向其中添加另外一个叫做 ALERTER 的 key。无论你搜索 alerter 还是 ALERTER,都会找到叫做 Alerter 的 key。
为了配置 Dictionary 的模式,首先创建一个 Dictionary 对象实例,然后配置它的属性为如下的值:
0-设置成为二进制模式,这个是默认选项。
1 – 设置成 text 模式。
例如,如下的 4.44 的脚本设置 Dictionary 成 text 模式:
Listing 4.44 Configuring the Dictionary Object
1 Const TextMode = 1
2 Set objDictionary = CreateObject("Scripting.Dictionary")
3 pareMode = TextMode
在 Dictionary 有任何元素的时候,你不能更改 Dictionary 的 CompareMode 属性。这是因为,二进制允许大小写的区分,例如,如下的
keys 每个代表了不同 key,因为 Dictionary 是基于二进制格式的:
apple
Apple
APPLE
在 text 模式下,这些 keys 是一致的。如果你将这些元素存储在二进制格式,那么没有什么问题,但是存储在 text 模式下的时候,Dictionary
就突然之间有三个同样的 keys,它就会失败了。
如果你想配置 Dictionary 的模式,那么事先移走 Dictionary 中所有的元素。
Adding Key-Item Pairs to a Dictionary
在创建了 Dictionary 对象实例之后,你可以用 add 方法向 Dictionary 添加 key-item 对。Add方法需要两个参数,必须按照如下顺序并且用逗号分开: Key name , Item value
例如,在脚本 4.45 中,创建了一个 Dictionary 对象,然后添加一些类似于表格 4.10 中的key-item 对。
Table 4.10 Sample Key-Item Pairs
Key Item
Printer 1 Printing
Printer 2Offline
Printer 3Printing
Listing 4.45 Adding Key-Item Pairs to a Dictionary
1 Set objDictionary = CreateObject("Scripting.Dictionary")
2 objDictionary.Add "Printer 1", "Printing"
3 objDictionary.Add "Printer 2", "Offline"
4 objDictionary.Add "Printer 3", "Printing"
Dictionary 的 key 必须是唯一的。例如,如下的两个语句会产生一个运行的错误出来。在第一行被编译之后,叫做 printer 1 的 key 已经存在于 Dictionary 中了:
objDictionary.Add "Printer 1", "Printing"
objDictionary.Add "Printer 1", "Offline"
Inadvertently Adding a Key to a Dictionary
使用 Dictionary 对象的一个潜在的问题是,你如果去访问一个在 Dictionary 中不存在的元素的时候,它不产生一个错误,而是将这个不存在的元素添加到 Dictionary 中去了。看下面的脚本示例,它创建了一个
Dictionary,添加了三对 key-item,然后尝试去回显不存在的item,printer 4:
Set objDictionary = CreateObject("Scripting.Dictionary")
objDictionary.Add "Printer 1", "Printing"
objDictionary.Add "Printer 2", "Offline"
objDictionary.Add "Printer 3", "Printing"
Wscript.Echo objDictionary.Item("Printer 4")
当脚本尝试去回显不存在的 item 的时候,没有任何运行错误发生,而是创建了一个新的 key,printer 4,并被添加到了 Dictionary 中,它的对象的 item 的值是 null。
为了避免这样的错误发生,在访问 item 的值的时候,去检查它是否存在。
Manipulating Keys and Items in a Dictionary
作为 Dictionary 本身而言,它的用途很少。一个 Dictionary 只有在访问它的 item,列举它的item,或者修改它的 item 的时候,才变得有用。在你创建了一个 Dictionary 之后,你可能作如下的一些事情:
确定在这个 dictionary 中有多少对 key-item 对列举出在 dictionary 的 keys 和或者 item。
决定在 Dictionary 是否存在特定的 key修改在 Dictionary 中的 key 或者 item 的值从 Dictionary 中移除特定 key-item 对。
所有这些任务都可以通过在 RunTime Library 中提供的属性方法来实现。
Determining the Number of Key-Item Pairs in a Dictionary
和其它的许多集合一样,Dictionary 有一个 count 属性用来返回在这个集合中的 key-item 对的数量。在脚本 4.46 中创建了是个
Dictionary 对象,然后添加三个 key-item 对,回显这个Dictionary 的 count 属性的值。
Listing 4.46 Determining the Number of Key-Item Pairs in a Dictionary
1 Set objDictionary = CreateObject(“Scripting.Dictionary”)
2 objDictionary.Add “Printer 1”, “Printing”
3 objDictionary.Add “Printer 2”, “Offline”
4 objDictionary.Add “Printer 3”, “Printing”
5 Wscript.Echo objDictionary.Count
当脚本运行的时候,会显示一个值 3
Enumerating Keys and Items in a Dictionary
Dictionary 被设计成用来暂时的存储信息。任何存在 Dictionary
中的信息都是作为临时用的,没有用来长期存储的。它一般是用来临时放信息,然后在脚本中在重新调用他们。例如,你有个存储了服务器名称清单的Dictionary,然后在后面的脚本中连到这些服务器上,并且获得相应的信息。结果是,你每次连接到一个服务器的时候,你都必须去调用 Dictionary中的信息。
Keys items 的方法可以用来返回他们的组成集合,或者的单独的返回或者全部返回在Dictionary 中的 item-key 的信息。在你用了其中的一个方法之后,你可以用 For Each 循环列举除在数组中所有的 key-item。
例如在 4.47 中的脚本创建一个简单的 Dictionary,添加三对 keys-item,在这个 Dictionary 创建结束之后,脚本用 keys 方法来列举出所有的 keys,然后用 items 方法来返回所有的 items
Listing 4.47 Enumerating Keys and Items in a Dictionary
1 Set objDictionary = CreateObject("Scripting.Dictionary")
2 objDictionary.Add "Printer 1", "Printing"
3 objDictionary.Add "Printer 2", "Offline"
4 objDictionary.Add "Printer 3", "Printing"
5
6 colKeys = objDictionary.Keys
7 For Each strKey in colKeys
8 Wscript.Echo strKey
9 Next
10
11 colItems = objDictionary.Items
12 For Each strItem in colItems
13 Wscript.Echo strItem
14 Next
当脚本在 CScript 下运行的时候,命令行的信息如下:
Printer 1
Printer 2
Printer 3
Printing
Offline
Printing
为了显示特定的 item 的值,用 item 方法,例如,如下的代码语句显示了 key 是 printer 3 的item 的值:
Wscript.Echo objDictionary.Item("Printer 3")
Verifying the Existence of a Specific Key
Dictionary 的一个比数组更加优秀和标准的优点是你可以快速的找到特定的 key 是否存在。例如,假如你有一定的文件 list 安装在一个计算机上,并且你向搜索这个 list,来保证特定的dll文件已经安装了。你用集合或者数组,除非你系统的搜索整个数组否则你没有方法来确定特定的一个文件是否存在了。但是用 Dictionary 你可以 Exists 方法来检查给定的 key 是否存在。用Exists 方法只是需要一个参数,(需要查询的 key 的名字),然后返回一个 Boolean 的值,如果是真的值,表明这个 key 存在,如果是否,则不存在。
例如,在脚本 4.48 中创建一个 Dictionary 对象,添加三对元素,然后脚本检查叫做 key 的关键词是否存在,并且回显其结果。
Listing 4.48 Verifying the Existence of a Dictionary Key
1 Set objDictionary = CreateObject("Scripting.Dictionary")
2 objDictionary.Add "Printer 1", "Printing"
3 objDictionary.Add "Printer 2", "Offline"
4 objDictionary.Add "Printer 3", "Printing"
5 If objDictionary.Exists("Printer 4") Then
6 Wscript.Echo "Printer 4 is in the Dictionary."
7 Else
8 Wscript.Echo "Printer 4 is not in the Dictionary."
9 End If
当脚本运行的时候,会有 printer 4 is not in the Dictionary 的消息出现在屏幕上.
Modifying an Item in a Dictionary
添加到 Dictionary 中的 item 不是一成不变的,事实上,你可以在任何的时间去更改它。这个功能可以让你纪录脚本的运行情况。例如,你的Dictionary 是个关于服务器名称的列表区,每次你的脚本对应的服务器运行结束之后,你可以更改它对应的 item 值来标识它的状态。在脚本中止之前,你可以将状态列出来看脚本运行成功与否的。在 4.49 中的脚本创建了一个具有三个 keys 的 Dictionary,这三个key 分贝为 atl-dc-01,atl-dc-02,atl-dc-03,每个的 item 都是设置成 no status,这个表明针对这个服务器的状态信息还没有获得。然后回显他们的 item的信息。
在这个 Dictionary 被创建之后,用 item 方法以 key 作为参数更改这些 item 的值。例如,如下的脚本代码语句更改 key atl-dc-01 的 item 为 available::
objDictionary.Item("atl-dc-01") = "Available"
在这三个 items 的值被更改了之后,新的 item 被回显出在屏幕上:
Listing 4.49 Modifying the Value of a Dictionary Item
1Set objDictionary = CreateObject("Scripting.Dictionary")
2objDictionary.Add "atl-dc-01", "No status"
3objDictionary.Add "atl-dc-02", "No status"
4objDictionary.Add "atl-dc-03", "No status"
5
6colKeys = objDictionary.Keys
7For Each strKey in colKeys
8Wscript.Echo strKey, objDictionary.Item(strKey)
9Next
10
11 objDictionary.Item("atl-dc-01") = "Available"
12 objDictionary.Item("atl-dc-02") = "Available"
13 objDictionary.Item("atl-dc-03") = "Unavailable"
14
15 colKeys = objDictionary.Keys
16 For Each strKey in colKeys
17 Wscript.Echo strKey, objDictionary.Item(strKey)
18 Next
当在 Cscript 下运行这个的时候,命令行中的信息如下:
atl-dc-01 No status
atl-dc-02 No status
atl-dc-03 No status
atl-dc-01 Available
atl-dc-02 Available
atl-dc-03 Unavailable
Removing Key-Item Pairs from a Dictionary
Key-item 对也可以从 Dictionary 中移走,Script Runtime Library 提供了移走 key-item 对的两种方法:
RemoveAll, 移走所有的 key-item 对
Remove, 从 Dictionary 移走特定的 key-item 的值。
Removing All Key-Item Pairs from a Dictionary
你或许有一些脚本,比如监视的脚本,作着同样的动作,将信息存在一个 Dictionary 中,然后要么在屏幕上显示这些信息,要么将这些信息存储在文本文件中或者数据库中。然后,脚本停止几分钟,收集更新了之后的信息,就这样不确定的进行下去了。
如果你用 Dictionary 来作为临时的数据存储收集信息数据,你可能希望在收取新的信息之前清空 Dictionary 的内容。为了做到这点,你可以用方法RemoveAll 来移走所有的 Dictionary中的 key-item 对。例如,在 4.50 中的脚本创建了一个含有三个元素的 Dictionary,然后显示 Dictionary 的 keys,脚本用 RemoveAll 方法移走所有的 key-item对,Dictionary.RemoveAll 为了检查元素是否被移走,在此回显 Dictionary 中 key 的信息Listing 4.50 Removing All the Key-Item Pairs in a Dictionary
1 Set objDictionary = CreateObject("Scripting.Dictionary")
2 objDictionary.Add "Printer 1", "Printing"
3 objDictionary.Add "Printer 2", "Offline"
4 objDictionary.Add "Printer 3", "Printing"
5 colKeys = objDictionary.Keys
6 Wscript.Echo "First run: "
7 For Each strKey in colKeys
8Wscript.Echo strKey
9 Next
10 objDictionary.RemoveAll
11 colKeys = objDictionary.Keys
12 Wscript.Echo VbCrLf & "Second run: "
13 For Each strKey in colKeys
14 Wscript.Echo strKey
15 Next
当脚本在 CScript 运行的时候,如下的信息显示在命令行中。和你看见的一样,第二次输出的时候是空的,因为 Dictionary 已经没有任何的 keys 了。
First run:
Printer 1
Printer 2
Printer 3
Second run:
Removing a Specific Key-Item Pair from a Dictionary
和移走所有的 key-item 对不同,你有的时候可能向移走一个单独的 key 和item。例如,假如你的脚本来从一系列的计算机上获得时间日志。脚本被设计成尝试联系每个计算机跟踪每个成功和失败。如果不能连接到计算机上,脚本尝试重新连接,知道连接到计算机并获得所有的日志信息。在这个假想的情况下,可能你的
Dictionary 中有如下的 keys:
atl-dc-01
atl-dc-02
atl-dc-03
atl-dc-04
atl-dc-05
当脚本运行的时候,它可能不能连接到计算机
atl-dc-03,atl-dc-04,在这种情况下,脚本去尝试重新连接这些计算机。但是脚本怎么知道哪个计算机需要连接,哪个不需要那?
一个解决的办法就是简单的移走已经成功连接的计算机。在第一段脚本被编译结束之后,那这个Dictionary 中只是包含如下的元素:
atl-dc-03
atl-dc-04
一旦一个计算机连接成功,就从 Dictionary 中移走这个元素,这样,如果 Dictionary 中不再含有任何元素的时候,那么所有的计算机就都连接成功了,脚本运行结束了。
为了移走单个的元素,用 Remove 方法,输入 key 的名字作为唯一的参数。例如如下的脚本语句移走了 key 为 atl-dc-02 的 key 和它对应的item。
objDictionary.Remove("atl-dc-02")
在 4.51 中的脚本创建一个有三个元素的 Dictionary 对象,回显所有的 keys。然后移走 key为 printer 2 的 key 和它的item,然后再次回显 Dictionary 的 keys:
Listing 4.51 Removing a Specified Key-Item Pair from a Dictionary
1 Set objDictionary = CreateObject("Scripting.Dictionary")
2 objDictionary.Add "Printer 1", "Printing"
3 objDictionary.Add "Printer 2", "Offline"
4 objDictionary.Add "Printer 3", "Printing"
5 colKeys = objDictionary.Keys
6 Wscript.Echo "First run: "
7 For Each strKey in colKeys
8Wscript.Echo strKey
9 Next
10 objDictionary.Remove("Printer 2")
11 colKeys = objDictionary.Keys
12 Wscript.Echo VbCrLf & "Second run: "
13 For Each strKey in colKeys
14 Wscript.Echo strKey
15 Next
当脚本在 CScript 运行的时候,在命令行中显示如下:
First run:
Printer 1
Printer 2
Printer 3
Second run:
Printer 1
Printer 3
在第二段脚本编译的末尾,printer 2 已经不再存在于 Dictionary 中了。