Share via


嗨,Scripting Guy!

Hey,Scripting Guy!

歡迎使用全新的 TechNet 專欄,Microsoft Scripting Guy 會在此為您解答有關系統管理指令碼的常見問題。您有關於系統管理指令碼方面的問題嗎?請將電子郵件傳送到 scripter@microsoft.com。我們無法保證能夠逐一回答每個問題,不過我們會盡力而為。

今天的問題:我要如何建立 CSV 檔案?


我要如何建立 CSV 檔案?

嗨,Scripting Guy!我要如何建立 CSV 檔案?

-- LF

LF,您好。讓我們針對不太瞭解縮寫的人說明,CSV 是逗號分隔值檔案的縮寫,這是其中個別元素由逗號分隔的一種文字檔。例如:假設您擁有使用者名字、姓氏和工作職稱所組成的 CSV 檔案,其內容可能會和以下相似:
Ken,Myer,Accountant
Pilar,Ackerman,Vice-President
Carol,Philips,Research Specialist

順便一提,在 Microsoft 我們非常喜愛縮寫。例如:有一天 Scripting Guy 收到 3 個句子的產品描述,其中含有 11 個縮寫字。我們個人的最愛,並在工作環境中最廣為使用的是 OOF,也就是 Out Of Office (離開辦公室) 的縮寫。

無怪乎我們覺得需要對所有的產品增加拼字檢查程式。

不過回到您的問題。您要如何自行建立 CSV 檔案?這是很容易的:只需要使用 FileSystemObject。畢竟,這就是這個類別的作用。

讓我們先參閱一段簡單的示範指令碼,然後再參閱更為實用的範例。以下的範例指令碼將字串 A,B,C 寫入名為 Test.csv 的文字檔:

Const ForWriting = 2
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objLogFile = objFSO.CreateTextFile("test.csv", _ 
    ForWriting, True)
objLogFile.Write "A," 
objLogFile.Write "B," 
objLogFile.Write "C"
objLogFile.Writeline
objLogFile.Close

我們一開始定義名為 ForWriting 的常數,並將其值設定為 2。在每次使用 FileSystemObject 時,您都要根據是否要對檔案讀取、寫入或是附加內容,而使用適當的常數。我們建立 FileSystemObject 的例項,然後使用 CreateTextFile 方法建立名為 Test.cvs 的新文字檔 (請注意,我們沒有指定路徑;這表示文字檔會建立在與指令碼相同的資料夾中。如果想要的話,我們可以指定完整的路徑,就像是 C:\Scripts\Logfiles\Test.csv)。

在呼叫 CreateTextFile 方法的同時,我們建立對新檔案的物件參照。在指令碼中,我們將此物件參照命名為 objLogFile (雖然我們可以使用任何想用的名稱)。有了物件參照之後,我們就可以使用 Write 方法將資料寫入檔案。請注意,我們從寫入字串 A, 開始 (沒錯,在建立 CSV 檔案時,我們必須以手動包含逗號)。Write 方法會寫入指定的資料 (在此情況下為 A, ),然後將游標留在原位置上。因此,在下次呼叫此方法時,字串 B, 就會跟在 A, 的右方。結果,文字檔就會成為以下的樣子:

A,B,

我們第三次呼叫 Write 方法,並寫入 C。由於這次標記一列的結尾,我們不在之後附加逗號,而是呼叫 WriteLine 方法,此方法相當於在鍵盤上按下 ENTER 鍵。於是我們得到如下的文字檔:

A,B,C

請注意,在逗號及下個項目的開頭之間,並沒有空格存在。在一個項目結束時,下一個項目就會立刻開始。

現在讓我們來看更為實用的範例。這段指令碼使用 WMI 擷取服務資訊,然後使用 FileSystemObject 將該資訊寫入名為 Service_List.csv 的文字檔:

Const ForAppending = 2
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objLogFile = objFSO.CreateTextFile("service_list.csv", _ 
    ForWriting, True)
strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
    & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colListOfServices = objWMIService.ExecQuery("Select * from Win32_Service")
For Each objService in colListOfServices
    objLogFile.Write objService.Name & "," 
    objLogFile.Write objService.StartMode & "," 
    objLogFile.Write objService.State 
    objLogFile.Writeline
Next
objLogFile.Close

如您所見,我們在示範指令碼中使用完全相同的技巧。最大的差別在於我們沒有使用如 A, B, 和 C 的固定值。而是使用如 objService.Name 的變數。這樣做不會有問題,我們只要將變數當做 Write 方法的參數使用即可。不過由於這些是變數,我們也就無法使用雙引號包含這些和後續的逗號。如下列這行程式碼,就「不會」有任何作用:

objLogFile.Write "objService.Name,"

因此,我們指定變數 (objService.Name),然後使用 "&" 符號在其結尾接上逗號 (",")。我們對 StartMode 也使用相同的做法,不過對 State 就不必這樣。這是因為 State 是每行的最後一個項目。結果,我們使用 WriteLine 在檔案中按下 ENTER 鍵並開始新的一行 (這和第一個範例中的字母 C 以後使用 WriteLine 的情況相同)。由於這些都是在 For Each 迴圈之中進行的,我們就會對電腦上安裝的每項服務寫入這筆資訊。

最後的結果是和以下相似的文字檔:

Alerter,Manual,Stopped
ALG,Manual,Stopped
AppMgmt,Manual,Stopped
aspnet_admin,Auto,Stopped

雖然我們可以稍微減少程式碼的行數 (藉由連結所有變數和其他種種項目),以這種風格撰寫程式碼,能讓您更容易地看清其中的作用。而且這樣也讓您能更容易地在每行新增項目。想要包含服務的 PathName (路徑名稱) 嗎?您只需要新增下列這行程式碼:

objLogFile.Write objService.PathName & ","

還有一點:當您使用 WMI 時,以上的指令碼應該能夠滿足您的所有需要。不過,在使用其他指令碼技術時,就有可能碰到關於包含逗號資料的問題。例如:假設您擁有包含使用者、他們的辦公室地址和工作職稱的文字檔。有一名使用者的地址為 2049,另一名使用者則有 2050, Suite A 的地址。就會產生如下的文字檔:

Ken,Myer,2049,Accountant
Pilar,Ackerman,2050,Suite A,Vice-President

唉呀!有問題了:第一行含有 4 欄 (請記得,逗號表示一欄的結尾,以及下一欄的開頭):Ken / Myer / 2049 / Accountant。不幸的是,由於地址本身含有逗號,第二行變成含有 5 欄:Pilar / Ackerman / 2050 / Suite A / Vice-President。糟糕…。

您要如何處理像這類的內嵌逗號呢?秘訣在於以雙引號包含每欄。如果您的文字檔看起來如下,內嵌的逗號就會遭到忽略:

"Ken","Myer","2049","Accountant"
"Pilar","Ackerman","2050,Suite A","Vice-President"

那您又要如何用雙引號將欄位括起來呢?以下是另一段範例指令碼,其中使用函式 Chr(34) 寫入一個雙引號、寫入服務屬性 (例如:objService.Name),再使用 Chr(34) 寫入另一個雙引號,「然後」才將逗號接到結尾。

Const ForWriting = 2
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objLogFile = objFSO.CreateTextFile("service_list.csv", _ 
    ForAppending, True)
strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
    & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colListOfServices = objWMIService.ExecQuery("Select * from Win32_Service")
For Each objService in colListOfServices
    objLogFile.Write chr(34) & objService.Name & chr(34) & "," 
    objLogFile.Write chr(34) & objService.StartMode & chr(34) & "," 
    objLogFile.Write chr(34) & objService.State  & chr(34)
    objLogFile.Writeline
Next
objLogFile.Close

如需詳細資訊,請參閱《Microsoft Windows 2000 指令碼指南》中的這個部分 (英文)。我們可以料想到您的下個問題:您要如何使用指令碼來「讀取」CSV 檔案?如果想要知道答案,請參閱這期關於使用 ADO 讀取文字檔的指令碼門診專欄 (英文)。


如需詳細資訊

查看嗨,Scripting Guy!- 過往文件

 

回到頁首 回到頁首