1. 背景
windosw updateをオフラインで実行する過程で、オフラインのwindowsマシンは、windows updateの状況が書かれたXMLファイルを出力する。
出力されるファイルは次のようなものである。
<XMLOut>
<CatalogInfo>
<CreationDate>2015-07-14T04:38:16Z </CreationDate>
</CatalogInfo>
<Check ID="500" Grade="2" Type="5" Cat="1" Rank="1" Name="Developer Tools, Runtimes, and Redistributables のセキュリティ更新プログラム" URL1="Help/Check5311.html" URL2="Help/Check5311fix.html" GroupID="48ce8c86-6850-4f68-8e9d-7dc8535ced60" GroupName="Developer Tools, Runtimes, and Redistributables">
<Advice>不足しているセキュリティ更新プログラムが 2 個あります。
</Advice>
<Detail>
<UpdateData ID="MS11-025" GUID="bb49cc19-8847-4986-aa93-5e905421e55a" BulletinID="MS11-025" KBID="2538242" Type="1" IsInstalled="false" Severity="3" RestartRequired="false">
<Title>Microsoft Visual C++ 2005 Service Pack 1 再頒布可能パッケージのセキュリティ更新プログラム (KB2538242)
</Title>
<References>
<BulletinURL>http://www.microsoft.com/technet/security/bulletin/MS11-025.mspx
</BulletinURL>
<InformationURL>http://go.microsoft.com/fwlink/?LinkId=216804
</InformationURL>
<DownloadURL>http://download.windowsupdate.com/msdownload/update/software/secu/2011/06/vcredist_x86_b8fab0bb7f62a24ddfe77b19cd9a1451abd7b847.exe
</DownloadURL>
</References>
</UpdateData>
<UpdateData ID="MS11-025" GUID="729a0dcb-df9e-4d02-b603-ed1aee074428" BulletinID="MS11-025" KBID="2538243" Type="1" IsInstalled="false" Severity="3" RestartRequired="false">
<Title>Microsoft Visual C++ 2008 Service Pack 1 再頒布可能パッケージのセキュリティ更新プログラム (KB2538243)
</Title>
<References>
<BulletinURL>http://www.microsoft.com/technet/security/bulletin/MS11-025.mspx
</BulletinURL>
<InformationURL>http://go.microsoft.com/fwlink/?LinkId=216803
</InformationURL>
<DownloadURL>http://download.windowsupdate.com/msdownload/update/software/secu/2011/05/vcredist_x86_470640aa4bb7db8e69196b5edb0010933569e98d.exe
</DownloadURL>
</References>
</UpdateData>
</Detail>
</Check>
(中略)
</XMLOut>
(もともとのファイルは改行があまりないので、blogに掲載するためにemacsのsgml-modeでsgml-pretty-printで整形した。)
ここで、各UpdateDataにおいて、IsInstalled="false"になっている項目を、DownloadURLからダウンロードしてくる必要がある。
さて、どうやろうか。手動でやるのは問題外である。
いろいろ方法はありそうだが、XMLのライブラリがあるscript言語を使ってみよう。
私が一番親しいscript言語は、Gaucheである。
2. Gaucheのxmlモジュール
GaucheにはXMLのためのモジュールがいくつかある。今回は、sxml.ssax、sxml.sxpathでやりたいことがやれた。
詳しくは、Referenceを参照のこと。[1]
まず、ssax:xml->sxmlでファイルから読み込んだXMLをS式(sxml形式と読んでいる)に変換する。
(define (read-xml file-name)
(call-with-input-file file-name
(lambda (port)
(ssax:xml->sxml port '() ))) )
は、XMLファイルを引数として受け取り、ファイル内のXMLと同じ構造のS式を返す関数を定義する。(call-with-input-file file-name
(lambda (port)
(ssax:xml->sxml port '() ))) )
ssax:xml->sxml の第3引数namespace-prefix-assigは
'()としたが問題は起きなかった。
次に、このS式を処理して必要なURLだけをファイルに書き出したい。
それにはsxpathが使える。
(sxpath '(XMLOut Check))は、(ルートから)XMLOut -> Checkとたどった時の要素のリスト(Checkがたくさんあることが想定されるから)を取り出すClosureを返す。
したがって、sxmlであるsxml-wholeに対して、((sxpath '(XMLOut Check)) sxml-whole)でXMLOut -> Checkとたどった時の要素のリストが返る。
3. まとめ
これらを組み合わせると、windowsの出力したXMLを食べて、必要なDownloadURLの一覧を出力するscriptができる。#!/usr/local/bin/gosh
(use sxml.ssax)
(use sxml.sxpath)
(define (read-xml file-name)
(call-with-input-file file-name
(lambda (port)
(ssax:xml->sxml port '() ))) )
(define (get-url-unless-installed update-data)
(if (string=? "false"
(cadar ((sxpath '(|@| IsInstalled)) update-data)))
(cadar
((sxpath '(References DownloadURL)) update-data))
#f))
(define (main argv)
(let ((sxml-whole (read-xml (list-ref argv 1))))
(map (lambda (y) (print y))
(filter (lambda (x) x)
(apply append
(map (lambda (check)
(map get-url-unless-installed
((sxpath '(Detail UpdateData)) check)))
((sxpath '(XMLOut Check)) sxml-whole))
)
))
))
これで、gaucheでxmlから特定の要素を抜き出すことができた。
[1] Gauche ユーザリファレンス: 12. ライブラリモジュール - ユーティリティ
http://practical-scheme.net/gauche/man/gauche-refj_149.html#g_t_00e3_0083_00a9_00e3_0082_00a4_00e3_0083_0096_00e3_0083_00a9_00e3_0083_00aa_00e3_0083_00a2_00e3_0082_00b8_00e3_0083_00a5_00e3_0083_00bc_00e3_0083_00ab-_002d-_00e3_0083_00a6_00e3_0083_00bc_00e3_0083_0086_00e3_0082_00a3_00e3_0083_00aa_00e3_0083_0086_00e3_0082_00a3