
TechNet コラムへようこそ。このコラムでは、よく寄せられるシステム管理スクリプトに関する質問に Scripting Guys がお答えします。システム管理スクリプトについて質問がある場合は、scripter@microsoft.com (英語のみ) までお送りください。すべての質問に回答することはできないかもしれませんが、可能な限り対応いたします。
詳細情報
| • | |
| • | |
| • |
![]()
Scripting Guy さん、よろしくお願いします。ディレクトリ ツリー内で、特定のフレーズを含むすべてのファイルを探す方法はありますか。
-- PB

PB さん、こんにちは。さて、次のようなことを想像してみてください。このコラムを執筆している Scripting Guy と彼の息子は、数か月ぶりにバスケットボールをしています。息子が大差でリードしていましたが、このコラムを執筆している Scripting Guy は猛烈に反撃し、4 回連続でスリーポイント シュートを決めて同点に持ち込みました。その日、ジムが閉館間近になったときの状況は、「次に得点を入れた方が勝ち」ということになっていました。
このとき、ボールを持っているのは、このコラムを執筆している Scripting Guy です。彼はエンド ラインに向かってドリブルをしますが、何かくだらない理由でドリブルをやめました (バスケットボールに興味のない方のために説明すると、エンド ラインの近くにいるときにドリブル中のボールを手に取ることはあまり良い行動ではありません)。そして、自分の愚かな行動により、このコラムを執筆している Scripting Guy は身動きが取れなくなっていました。彼の片側にはエンド ラインがあり、反対側には、父親よりゆうに 15 cm は背が高い息子が立ちはだかっています。すばやく考えながら (少なくとも Scripting Guy にとっての精一杯の速度で考えながら)、ある方向へピボットし、逆方向へピボットし、空中にジャンプしました。そして息子が広げた腕の下から、下手投げでしかも左手でシュートしました。すると、ボールはバックボードをガタガタ揺らして、ゴール リングに入りました。ここでゲーム セットです。
「すごく運が良かったね」と Scripting Guy の息子は言いました。
運ですって。どうみても、Scripting Guy の息子は運というものをわかっていないようです。バスケットボールでシュートを決まることは運ではありません。運が良いのは、ディレクトリ ツリー内で特定のフレーズを含むすべてのファイルを探す方法について質問を送ったら、たまたまちょうどぴったりのスクリプトが転がっているのを見つけた人のことです。
つまり、以下のようなスクリプトです。
On Error Resume Next
Set objConnection = CreateObject("ADODB.Connection")
Set objRecordSet = CreateObject("ADODB.Recordset")
objConnection.Open "Provider=Search.CollatorDSO;Extended Properties='Application=Windows';"
objRecordSet.Open "SELECT System.ItemPathDisplay FROM SYSTEMINDEX WHERE Contains('Alice') " & _
"AND System.ItemPathDisplay LIKE 'C:\Scripts\%'", objConnection
objRecordSet.MoveFirst
Do Until objRecordset.EOF
Wscript.Echo objRecordset.Fields.Item("System.ItemPathDisplay")
objRecordset.MoveNext
Loop
このスクリプトについて詳しく説明する前に、このスクリプトでは Windows デスクトップ サーチ 3.0 を使用していることをお知らせしておきます。これは、比較的新しく、完全にスクリプト化することが可能な検索テクノロジで、Windows Vista に同梱されています (他のバージョンの Windows では、ダウンロードしてインストールすることもできます)。この方法を使うことにした理由は 2 つあります。
| • | フォルダ ツリー内のすべてのフォルダにアクセスするには、デスクトップ サーチを使用した方が、WMI や FileSystemObject を使用するよりはるかに簡単です。WMI や FileSystemObject でこの処理を実行する場合、おかしな再帰的なサブルーチンを記述して、フォルダ ツリー内のすべてのフォルダを取得する必要があります。うわさにもかかわらず、Scripting Guys はおかしなことをするのは好きではありません (念のために言っておきますが、実際におかしなことをしないわけではありません。そのようなことをするのが好きではないだけです)。 |
| • | Windows デスクトップ サーチを使用すると、Word 文書、Excel ワークシート、PowerPoint プレゼンテーションなど、あらゆるファイルの種類を開いて検索できます。デスクトップ サーチを使用しないと、テキスト ファイル内の検索しかできません (確かに、理論上は他の種類の文書も検索できます。しかし、Word 文書を識別し、開いて、検索するコードと、Excel ワークシートを識別し、開いて、検索するコードと、PowerPoint プレゼンテーションを・・・。たいへんであることは、おわかりいただけますよね)。 |
とにかく、この処理を実行する方法は他にもあるでしょうが、私たちは、この処理を実行する最も簡単な方法はデスクトップ サーチだと判断しました。それに、少なくとも Scripting Guys にとって、最も簡単な方法は大抵最善の方法です。
では、デスクトップ サーチを使用してこの処理を実行するにはどうすればよいでしょうか。まず、2 つの ADO (ActiveX Data Objects) オブジェクトを作成します。ADODB.Connection オブジェクトは、デスクトップ サーチのファイルのインデックスに接続するのに使用し、ADODB.Recordset オブジェクトはクエリで返されるあらゆる情報の格納場所として使用します。2 つのオブジェクトを作成したら、Connection オブジェクトの Open メソッドを使用して Windows デスクトップ サーチにバインドします。
objConnection.Open "Provider=Search.CollatorDSO;Extended Properties='Application=Windows';"
注 : このコード行では一体どのような処理が行われているのでしょうか。正直なところ、私たちにもよくわかりません。それに、あまり気にしてもいません。なぜなら、このコードが定型コードだからです。デスクトップ サーチを使用するときには、このコードをそのまま使用すれば、万事めでたしめでたしというわけです。 |
データ ストアを開いたら、ここからが検索スクリプトの核心です。この部分では検索対象を指定します。ここでは、Recordset オブジェクトの Open メソッドを呼び出して、メソッドに 2 つのパラメータを渡します。1 つ目は検索条件を含む SQL クエリで、2 つ目は Connection オブジェクト (objConnection) へのオブジェクト参照です。
objRecordSet.Open "SELECT System.ItemPathDisplay FROM SYSTEMINDEX WHERE Contains('Alice') " & _
"AND System.ItemPathDisplay LIKE 'C:\Scripts\%'", objConnection
私たちの考えでは、このコマンドで本当に重要な部分は、SQL クエリそのものです。
"SELECT System.ItemPathDisplay FROM SYSTEMINDEX WHERE Contains('Alice') " & _
"AND System.ItemPathDisplay LIKE 'C:\Scripts\%'"
このスクリプトで行われている処理をご説明しましょう。今日の目的は、Alice という単語を含むあらゆるファイルのファイル パスを取得することだけです (ちなみに、この検索では大文字と小文字が区別されません。Alice、alice、ALICE、または他の形式のこの単語が検出されます)。デスクトップ サーチを使用する場合は、取得するプロパティ値をすべて指定する必要があります。ファイルパスは System.ItemPathDisplay プロパティに含まれているので、クエリの冒頭部分は次のようになります。
SELECT System.ItemPathDisplay
クエリの次の部分 (FROM SYSTEMINDEX) も定型コードです。というのも、デスクトップ サーチでクエリできるテーブルは 1 つだけ (SYSTEMINDEX) だからです。次は WHERE 句です。この句には次の 2 つの条件があり、返されるファイルは両方の条件を満たしている必要があります。
| • | ファイルには Alice という単語が含まれている必要があります。このため、Contains('Alice') という構文を使用しています。この構文は、ファイルの内容を読み取って、読み取った内容の中に Alice という単語があるかどうか確認するようスクリプトに指示しています (厳密には、デスクトップ サーチで既にファイルを読み取っているので、スクリプトで読み取る必要はありません。でも、しくみは、おわかりいただけますよね)。 |
| • | ファイルは C:\Scripts フォルダ ツリー内にある必要があります。System.ItemPathDisplay LIKE 'C:\Scripts\%' という構文は、ファイル パス (System.ItemPathDisplay) が C:\Scripts\ で始まっている必要があることを示します。LIKE 演算子を使用すると、クエリでワイルドカードを使用できます。ここでのワイルドカードはパーセント記号 (%) で、dir コマンド (たとえば、dir C:\Scripts\Test.*) などのアスタリスクと同じ役割を果たしています。つまり、パーセント記号を使用すると、この句は「パスが C:\Scripts\ で始まるファイルを、(もし存在する場合は) C:\Scripts\ 以降のパスに関係なく、すべて表示しなさい」と解釈されます。その結果、C:\Scripts フォルダ内のファイル (たとえば、C:\Scripts\Test.txt) と C:\Scripts のサブフォルダ内のファイル (たとえば、C:\Scripts\Subfolder1\Test.txt) を探すことができます。 |
再帰的なサブルーチンを記述して、フォルダ ツリー内のすべてのファイルにアクセスするという一見単純そうな同じ作業を実行するよりもずっと簡単ですね。
クエリを実行したら、MoveFirst メソッドを使用して返されたレコードセットの最初のレコードに移動します (この処理が本当に必要かどうかは確信がありませんが、用心するに越したことはありませんよね)。次に、レコードセットの終わりに達するまで実行される Do Until ループを設定します。つまり、レコードセットの EOF (ファイルの終わり) プロパティが True になるまで実行されるように設定します。
Do Until objRecordset.EOF
このループ内ではそれほど多くの処理をしません。System.ItemPathDisplay プロパティの値をエコー バックしてから、MoveNext メソッドを呼び出してレコードセット内の次のレコードに移動して同じ処理を繰り返します。
Wscript.Echo objRecordset.Fields.Item("System.ItemPathDisplay")
objRecordset.MoveNext
最終的には、次のようなレポートが返されます。
C:\Scripts\Scores.xls C:\Scripts\Alice2.txt C:\Scripts\Decoded.txt C:\Scripts\Decrypt.txt C:\Scripts\New Folder\New Folder\New Text Document.txt
ここでは 2 つの点に注目してください。1 つ目は、一覧の 1 行目が、Excel ワークシート ファイルを示していることです。既に説明したように、デスクトップ サーチでは Microsoft Office ドキュメント内を検索することが可能です (特に、デスクトップ サーチでは Adobe .PDF ファイル内も検索できます)。2 つ目は、一覧の最後のファイルが C:\Scripts のサブフォルダにあるファイルであることです。つまり、本当に C:\Scripts フォルダ ツリー全体を検索していることがおわかりいただけると思います。
PB さん、これで問題を解決できるはずです。繰り返しになりますが、他にもこの処理を実行する方法はあるでしょう。しかし、デスクトップ サーチを使用すると非常に高速かつ簡単にこの処理を実行できます。デスクトップ サーチには他にもたくさんのすばらしい機能があるので、とにかく検討する価値があります (詳細とサンプル コードについては、私たちが執筆した「Seek and Ye Shall Find」(英語) という記事を参照してください)。
ご参考までに、Scripting Guy の息子は正しかったことを白状して認める必要があるでしょう。Scripting Guy に勝利をもたらしたシュートは、本当にただ運が良かっただけでした。実際、このコラムを執筆している Scripting Guy は、シュートをねらってさえいませんでした。おそらく、単に投げたボールがバックボードに当たって、そのリバウンドが取れたらいいと思っていただけでしょう。ボールが本当にゴール リングに入ったとき、一番驚いていたのはこの Scripting Guy 自身でしたから。
でも、いいですか、ここだけの話ですよ。このコラムを執筆している Scripting Guy がどれほど運が良かったか、Scripting Guy の息子が知る必要はありません。今回だけは、父親が本当にちゃんと狙ってシュートしたと思わせておきましょう。
おっしゃるとおりです。こんなことが起きるチャンスなんてそれほど多くないのでしょうね。
当たり前ですね。