Share via


嗨,Scripting Guy!對於追車... 還有 XML 的迷戀

Microsoft Scripting Guys

下載本文程式碼: HeyScriptingGuy2007_02.exe (150KB)

「我也不知道 為什麼我的狗一直要追車子」,相信大家都知道這個老掉牙的笑話。「因為,萬一牠真的逮到一部車子之後又能怎樣呢?」

問的好,但這是 Scripting Guys 無法回答的問題。除非逮到車的,剛好是住在我們巷口的 Lucy。我們都知道「牠」一定會趁機駕車去搶劫便利商店,並開車逃離現場。而且,根據家門前車道上的狀況,我們深信她這一路上必定是一路逃亡,不曾下車方便 (如果您聽懂我們話中含意的話)。

如果我們要將這老掉牙的笑話套用在現今的高科技時代,就可以說:「我也不知道為什麼我們的系統管理員一直要學 XML。因為,萬一她真的學會之後又能怎樣呢?」

注意囉,我們只有說我們要嘗試將老掉牙的笑話套用在當下的事件上,並沒有說會很好笑呀。

老實說,XML 技術可能真的被吹捧得太過頭了,至少在系統管理方面的確有不足之處。事實上,許多系統管理員這麼久以來也都過得好好的,完全不需要撰寫指令碼與 XML 檔案互動,而且近期內也不會有太大的變化。

話雖如此,但確實是有越來越多應用程式開始採用 XML 做為儲存資料的標準架構。原因很簡單。XML 資料檔案只是盛裝打扮過的文字檔,所以不但易於建立、可跨平台、而且不需要使用複雜 (更別提昂貴) 的資料庫程式。您有作業系統以及文字編輯器嗎?有的話就可以建立 XML 資料庫囉。

這也代表系統管理員至少可以在此方面運用 XML:撰寫指令碼來查詢 XML 檔案,把檔案當做全功能的資料庫使用。如此一來,Scripting Guys 便成為適合提供小秘訣的最佳人選了。

說得好。我們猜想,Lucy 一定正忙著在別人家的花園裡挖洞,才沒這閒工夫來寫 XML 的專欄。

讓我們先來看一個簡單的 XML 檔案 (請參閱 [圖 1]),其中的資料庫檔案包含四組指令碼,用於<嗨,Scripting Guy!專欄>。

Figure 1 嗨,Scripting Guy!以 XML 撰寫指令碼

<?xml version="1.0" encoding="ISO-8859-1"?>
<Repository>
  <Script>
    <Category>Microsoft Office</Category>
    <Subcategory>Microsoft Access</Subcategory>
    <Keyword>databases</Keyword>
    <Title>How Can I Print a Microsoft Access Report?</Title>
    <URL>https://www.microsoft.com/technet/scriptcenter/resources/qanda/oct06/hey1020.mspx</URL>
  </Script>
  <Script>
    <Category>Microsoft Office</Category>
    <Subcategory>Microsoft Access</Subcategory>
    <Keyword>databases</Keyword>
    <Title>How Can I Compact a Microsoft Access Database?</Title>
    <URL>https://www.microsoft.com/technet/scriptcenter/resources/qanda/oct06/hey1009.mspx</URL>
  </Script>
  <Script>
    <Category>Microsoft Office</Category>
    <Subcategory>Microsoft Word</Subcategory>
    <Keyword>hyperlinks</Keyword>
    <Title>How Can I Change an Existing Hyperlink in a Microsoft Word Document?</Title>
    <URL>https://www.microsoft.com/technet/scriptcenter/resources/qanda/oct06/hey1016.mspx</URL>
  </Script>
  <Script>
    <Category>Enterprise Servers</Category>
    <Subcategory>Microsoft SQL Server</Subcategory>
    <Keyword>databases</Keyword>
    <Title>How Can I Create a Table in a SQL Server Database?</Title>
    <URL>https://www.microsoft.com/technet/scriptcenter/resources/qanda/oct06/hey1016.mspx</URL>
  </Script>
</Repository>

如您所見,這是一個蠻簡單的小檔案。每一組指令碼都置於個別的 <Script> 標記之下。從現在開始,讓我們將這些指令碼稱做記錄。您想的沒有錯 -- 真正的 XML 迷絕對不會將這些項目稱為記錄。但是沒關係啦;我們的目標只是要讓您了解如何將大家都熟悉的技巧 (資料庫的查詢),套用到新的、不同的資料來源。我們是想說先專注於如何達成目標,以後有機會再回來討論這個主題,並介紹正確的術語。

好,讓我們回到所謂的記錄。每一筆記錄都包含下列欄位:<Category>、<Subcategory>、<Keyword>、<Title> 及 <URL>。

看來您並不覺得這有什麼了不起。或許我們應該讓您看看另一個指令碼,其中可以開啟這個 XML 檔案並以 Echo 傳回下列內容:

Set xmlDoc = CreateObject("Microsoft.XMLDOM")
xmlDoc.Async = "False"
xmlDoc.Load("C:\Scripts\Scripts.xml")

Set colNodes = xmlDoc.selectNodes _
("/Repository/Script/*")

For Each objNode in colNodes
   Wscript.Echo objNode.Text
Next

請注意:這些標記名稱有區分大小寫。它必須是「/Repository/Script」,而不是「/repository/script」或「/REPOSITORY/SCRIPT」。

雖然這也沒什麼了不起的。但這就是重點啊:其實這並沒有您想像的那麼難。

如您在程式碼中所見,我們一開始會建立 Microsoft.XMLDOM 物件的執行個體,亦即用來處理 XML 檔案的 COM 物件。然後我們將 Async 屬性設定為 False;這可以確保整個檔案會先讀取到記憶體中,才將控制權交回給指令碼。接著我們使用 Load 方法來讀取 C:\Scripts\Scripts.xml 檔案:

xmlDoc.Load("C:\Scripts\Scripts.xml")

一旦檔案載入記憶體之後,我們就可以使用 selectNodes 方法來選取資料庫中的特定記錄:

Set colNodes = xmlDoc.selectNodes _
("/Repository/Script/*")

請仔細看一下傳遞給 selectNodes 的參數。其實這代表 XML 檔案中的路徑。我們的 XML 檔案具有下列結構:

<Repository>
    <Script>
        <Category></Category>
        <Subcategory></Subcategory>
        <Keyword></Keyword>
        <Title></Title>
        <URL></URL>
    </Script>
</Repository>

您是否想問:我需要知道這些嗎?簡單來說,有需要!使用 XML 檔案時,結構很重要。例如,我們要如何存取記錄的個別屬性?其實很簡單:我們只要存取 <Repository> 標記,然後存取 <Script> 標記,再存取個別的屬性標記即可。您猜發生了什麼事?這個路徑和我們為 selectNodes 指定的參數是一樣的。我們只要在路徑後面加上 /*,這代表「選取這些記錄的所有屬性」。換句話說,傳回的集合將包含資料庫中所有記錄的所有屬性 -- 這和 Windows® Management Instrumentation (WMI) 中的 "Select * From" 查詢有異曲同工之妙。

請注意,先試用範例指令碼,您就會知道我們的意思了。

指令碼的其餘部分很簡單;我們只設定了一個 For Each 迴圈來瀏覽整個集合,然後再以 Echo 傳回每個項目和每個屬性的 Text 屬性:

For Each objNode in colNodes
   Wscript.Echo objNode.Text
Next

這比追車子更容易吧。

不過您想的沒錯:如果我們只是要以 Echo 傳回整個檔案的內容,根本就不需要用到 XML,僅需要使用 FileSystemObject 即可 (雖然這會稍微複雜一點,但只有一點點喔)。我們的用意是要讓您驚訝一下,讓您同意「哇,原來 XML 真的還蠻好用的」,但顯然我們未能到達那個階段。

讓咱們來想個辦法。在第一個指令碼中,我們傳回了所有記錄的所有屬性值。這沒問題,但是如果我們只想傳回指令碼的標題呢?這辦得到嗎?讓我問一下,請稍候片刻...

好,根據 Lucy 的說法,我們只需要修改 selectNodes 命令,將想要的屬性加到路徑的後面即可。也就是:

    Set colNodes=xmlDoc.selectNodes _    ("/Repository/Script/Title")

原來我們之前會傳回所有屬性,是因為我們使用了 * 萬用字元,而現在這樣就只會傳回 Title 屬性。原因何在?因為這是我們唯一要求傳回的屬性值。

請注意,最棒的是:XML 可以根據您的詳細要求提供回應。這下要請 Lucy 別傷心,因為我們已經開始認為 XML 才是人類最忠實的夥伴。

而且您當然可以傳回一個以上的屬性。舉例而言,下列修改過的命令就會同時傳回 Title 和 URL 屬性:

    Set colNodes=xmlDoc.selectNodes _    ("/Repository/Script/(Title | URL)")

我承認,雖然語法看起來有些怪異,但是習慣之後就沒什麼了。首先,我們指定路徑的父項部分,跟之前一樣:

    /Repository/Script/

然後我們再新增一組括號,並在其中指定要傳回的屬性 (請注意,我們使用縱線字元 (|) 來分隔個別的屬性)。也就是:

    (Title | URL)

我們說過,雖然看起來有些怪異,但的確有效。

而且您可以傳回的屬性並不侷限於兩個而已。只要您願意一直加入縱線分隔字元,就可以隨心所欲傳回想要的屬性。舉例而言,要傳回三組屬性的命令 (Title、URL 及 Keyword) 如下:

    Set colNodes=xmlDoc.selectNodes _    ("/Repository/Script/(Title | URL |          Keyword)")

很酷吧?

至少我們認為很酷。您還真難說服。但再一次的,您說的沒錯:我們目前只有在資料庫中擷取所有記錄的資訊而已。然而,這樣就已經不錯了。因為有時候,這就可以滿足您要完成的作業。另一方面,我們有時候 (其實是經常) 只想擷取記錄其中一個子集的資訊。舉例而言,我們可能只想要得到等於 databases 的 Keyword 之指令碼相關資訊。方法是使用如下的查詢:

    Set colNodes=xmlDoc.selectNodes _
    ("/Repository/Script " & _
    "[Keyword = ‘databases’]")

我知道,語法看起來有點怪。往好的方面想,語法其實很簡單明瞭。指定父項路徑之後,我們只要將 "Where" 子句 (例如:其中 x 等於 y) 放在方括內即可,如下所示:

/Repository/Script [Keyword = ‘databases’]

您可能已經看出來,我們的 Where 子句同等語法會告訴指令碼「只傳回 Keyword 屬性等於 databases 的項目」。請注意,databases 必須包含在單引號之間。這不但是好的習慣,更是必要的格式。如果篩選條件的關鍵字包含在雙引號之間,嘗試執行指令碼時就會發生語法錯誤。

請注意,為什麼不能這麼做?因為整個查詢字串—"/Repository/Script [Keyword = ‘databases’]"—已經包含在雙引號之間 (您應該記得吧)。

當然,您在篩選資料時,不僅可以使用等號 (=),還可以使用大於 (>) 或小於 (<) 符號,以及大家最愛的小於或等於 (<=) 和大於或等於 (>=) 符號。您也可以在符號前面加上驚嘆號,即可變成否定篩選條件。舉例而言,下列命令就會傳回所有 Keyword 不等於 databases 的指令碼 (請注意 != 的使用):

    Set colNodes=xmlDoc.selectNodes _
    ("/Repository/Script " & _
    "[Keyword != ‘databases’]")

等一下:誰膽敢說「可以設定篩選條件就了不起喔?我看一定沒辦法既設定篩選條件又指定要傳回的屬性吧」?當然沒問題:

    Set colNodes=xmlDoc.selectNodes _
    ("/Repository/Script " & _
    "[Keyword = ‘databases’]/Title")

如果一切正常,此命令應該會傳回所有指令碼有 Keyword 是 databases 的 Title 屬性。讓我們看看是否真的可行:

    How Can I Print a Microsoft Access Report?
    How Can I Compact a Microsoft Access Database?
    How Can I Create a Table in a SQL Server     Database?

結束前,讓我們再探索一個命令吧。雖然我們已經有相當程度的進展,可是系統可能還是會傳回過多的記錄。因為上述的命令其實會同時傳回 Microsoft® Access® 指令碼以及 Microsoft SQL Server™ 指令碼 (原因何在?因為這些指令碼都有 Keyword 是 databases)。如果我們想要把傳回的資料限制為 Keyword 有 databases 同時 Subcategory 是 Microsoft SQL Server 的指令碼,該怎麼做?可以使用多重準則進行篩選嗎?

這還用說:

    Set colNodes=xmlDoc.selectNodes _
    ("/Repository/Script " & _
    "[Keyword = ‘databases’ and " & _
    "Subcategory = ‘Microsoft SQL Server’]")

基本的語法都一樣,我們只是分別使用兩個準則 (亦即 Keyword = ‘databases’ 和 Subcategory = ‘Microsoft SQL Server’),然後再以 AND 運算子將兩者連接。

請注意,您沒有錯,確實可以:透過這個小小的 XML 檔案,我們可以要求 Subcategory 等於 Microsoft SQL Server 的所有指令碼。但只有那樣不夠好玩,而且也沒有教育價值。

您也可以使用 OR 子句。舉例而言,如果我們有一堆 Microsoft Office 指令碼,其中包括 Subcategory 等於 Microsoft Excel®、Microsoft PowerPoint®、Microsoft Outlook®...等等。我們是否可以僅傳回 Subcategory 為 Microsoft Word 或 Microsoft Access 的資料?當然可以:

    Set colNodes=xmlDoc.selectNodes _
    ("/Repository/Script " & _
    "[Subcategory = ‘Microsoft Word’ " & _
    "or Subcategory = ‘Microsoft Access’]")

這樣是不是像樣多了?或許我們這個專欄無法立即改變您的一生;但還是有機會讓您在未來發揮查詢 XML 檔案的技巧。學與不學當然需要由您自行選擇:您可以趁機學習一些基本的 XML 技巧,或是把時間用來追車。這之間的輕重由您自己決定 (萬一您選擇後者,而且剛好碰上住在巷口的狗狗 Lucy,麻煩轉告牠 Scripting Guys 跟牠問好,並請牠不要來我們的花園光顧!)。

嗨,Scripting Guy!- 天天樂翻天

您剛剛閱讀的是本月的<嗨,Scripting Guy!專欄>,相信您也會同意這是一篇偉大的技術文章。您甚至可能會說:這是至今最偉大的出版品。此外,您一定也是一直守在信箱旁邊,迫不及待想要收到下一期的 TechNet Magazine,才能夠閱讀更多類似這樣的文章,是吧。

那您在等什麼呢?想要進一步認識住在巷口的 Lucy 狗狗,或是閱讀 Scripting Son 最新的指令碼佳作嗎?您有聽說去年的 Turducken Bowl 結果嗎?只要您閱讀每天出刊的<嗨,Scripting Guy!專欄>(沒錯,是每天出刊喔),就可以得知這些重要資訊。週一到週五 (當然要扣除國定假日和 Scripting Guy 度假期間),您都可以閱讀到高中籃球賽事、大學美式足球賽事和籃球賽事,偶而還會來個當地氣象報告喔 (我必須承認,所謂的「當地」僅適用於 Redmond 的居民,但是話又說回來,Redmond 的居民們都不在乎氣象耶)。

不但如此,您每日還有可能會學習到指令碼的一些技巧。沒錯,在這多采多姿的內容中,的確還會有如何撰寫指令碼的資訊喔。在每一個專欄中,Scripting Guys 也會回答由 (應該是) 真人提出的問題。您可以閱讀每日專欄中的這些資訊,網址為 microsoft.com/technet/scriptcenter/resources/qanda。對了,由於他們已經回答過數以百計的問題,所以封存檔案中有大量的資料,而這也是絕佳的指令碼資訊來源 (請參閱 microsoft.com/technet/scriptcenter/resources/qanda/hsgarch.mspx (英文))。

您是否有問題想要問 Scripting Guys?只要將問題傳送到 scripter@microsoft.com,就有微乎其微的機率會收到答覆 (相對的,如果您沒有將問題傳送給他們,就絕對不會收到答覆,所以還是要把握機會。這和樂透不一樣,這不用錢,而且您接收到答覆的機率會比中頭獎還要高喔)。

Microsoft Scripting Guys 為 Microsoft 做事,也就是受雇於 Microsoft。他們在不玩、不教或不看棒球 (以及其他各種活動) 的時候,就負責管理 TechNet Script Center。請到 www.scriptingguys.com 一探究竟。

© 2008 Microsoft Corporation and CMP Media, LLC. 保留所有權利;未經允許,嚴禁部分或全部複製.