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

DJ さん、こんにちは。突然ですが、DJ さんが私たちよりも良い 1 日を過ごされていることを願っています。Scripting Guys にとって今日はついてない 1 日なのか、ですって。そう言って差し支えないと思います。たとえば、今朝、このコラムを執筆している Scripting Guy は、毎日ドーナッツを買っている食料品店の前を急いで通り過ぎてしまいました。Scripting Guy がドーナッツを忘れたんですよ。それが悪い兆候でないとしたら、何を悪い兆候と言うのかわかりませんね。
注 : 心配しないでください、彼はくるりと身を翻して、店がある所まで戻ってドーナッツを買いました。当然、そのために少し遅刻しました。でも、優先事項は大事ですよね。 |
信じられないかもしれませんが、時間の経過と共に物事が悪化しているようです。実のところ、彼がそう言い張っているのは、ただ単にこのコラムが金曜日に執筆されたからでした。つまり、Scripting Guy がコラムを執筆したときは、翌日が土曜日だったということです。そして、土曜日はいつも良い日なのです。
まあ、ワシントン ハスキーズ (過去 4 試合だけで 170 点もの失点がありました) のフットボールの試合があるときを除いて、ということですが。しかし、彼らの試合は土曜日にあります。
まあ、日曜日は良い日になるかもしれません。何しろ日曜日の天気予報は・・・。ええと。気にしないでください。座っていじけていますから。
うーん。ただ座っていじけるのは思っていたほど楽しくないとわかりました (マイクロソフトではとても多くの人がそうするので、楽しいのだろうと思っていました)。では、ちょっと時間をつぶすために、他にできることがあるかどうか確認してみましょう。いい考えがあります。今日の質問の答えをご説明しましょう。
そうですね、仕方ありません。今日の質問に対する "正しい" 答えをご説明します。
その前に、今日のコラムの背景を簡単にご説明しましょう。DJ さんの電子メールによると、気温のデータを含んでいる一連のテキスト ファイルがあるそうです。次のようなテキスト ファイルです。
45.544 34.544 65.433 56.783 32.451 65.432 22.435. 44.564
DJ さんは、そのフォルダ内のすべてのファイルを処理して、各ファイルに対してファイル名をテキスト ファイルの 1 行目のデータとして挿入するスクリプトを必要としています。このようなスクリプトがあると、ファイルを別のソフトウェア プログラムにインポートできるのだそうです ("何という" ソフトウェア プログラムなのか、ですって。そんなことは聞かないでください。この間サード パーティ製のソフトウェア プログラムについて言及したときの出来事をまだ忘れていませんから)。つまり、DJ さんが必要なのは次のような一連のテキスト ファイルです。
apr_avg_temp 45.544 34.544 65.433 56.783 32.451 65.432 22.435. 44.564
でも、ローリング ストーンズが何と言ったか知っていますよね、DJ さん。欲しいものはいつも手に入るとは限りません。
しかし、さいわいにも、今回は欲しいものを手に入れることができます。ファイル名をテキスト ファイルの 1 行目のデータとして挿入し、フォルダ内のすべてのファイルに対して、この処理を行うスクリプトは次のとおりです。
Const ForReading = 1
Const ForWriting = 2
Set objFSO = CreateObject("Scripting.FileSystemObject")
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colFiles = objWMIService.ExecQuery _
("ASSOCIATORS OF {Win32_Directory.Name='C:\TemperatureData'} Where " _
& "ResultClass = CIM_DataFile")
For Each objFile in colFiles
strFileName = objFile.FileName
strFilePath = objFile.Name
Set objFile = objFSo.OpenTextFile(strFilePath, ForReading)
strContents = objFile.ReadAll
objFile.Close
Set objFile = objFSo.OpenTextFile(strFilePath, ForWriting)
strContents = strFileName & vbCrLf & strContents
objFile.Write strContents
objFile.Close
Next
注 : Scripting Guys はこれで少しは元気が出たのか、ですって。ええ、もちろんです。WMI と FileSystemObject の両方を使用するスクリプトによって、少しも元気にならない人はいないと思います。 |
このスクリプトのしくみはどうなっているのか、ですって。ご説明しましょう。スクリプトでは、まず ForReading と ForWriting という 2 つの定数を定義しています。この 2 つの定数は各テキスト ファイルからの読み取りと書き込みを行う場合に使用する必要があります。2 つの定数を定義したら、Scripting.FileSystemObject のインスタンスを作成します。このオブジェクトを使用して、テキスト ファイルからの読み取りと書き込みを実際に行います。
次に、ローカル コンピュータの WMI サービスに接続します。ところで、通常 WMI について書いたとき、その次には、このようなお決まりの文句が続いています。
「もちろん、このスクリプトはリモート コンピュータに対しても実行できます」
ただし、今回はそうではありません。今回は、次のような文になります。
「聞かれる前にお答えしますが、少なくとも、コードに大幅な変更を加えずに、このスクリプトをリモート コンピュータに対して実行することはできません」
では、なぜこのスクリプトをリモート コンピュータに対して実行できないのでしょうか。WMI はリモート コンピュータからファイルのコレクションを取得できないのでしょうか。
いいえ、WMI ではリモート コンピュータからファイルのコレクションを取得できます。このスクリプトをリモート コンピュータで実行できないのは WMI が原因ではありません。問題の原因は、ローカル コンピュータのローカル ファイルでのみ機能するよう設計された FileSystemObject オブジェクトにあります。この作業をリモート コンピュータで実行できないという意味ではありませんが、既に述べたように、そうするにはコードに大幅な変更を加える必要があります。このスクリプトをリモート コンピュータに対して実行することに興味がある方がたくさんいらっしゃる場合は、それを説明することについて検討しましょう。とりあえず、Hey, Scripting Guy! のこのコラムを参照してください。このコラムでは、リモート コンピュータ上にあるテキスト ファイルを操作する方法について説明しています。
WMI サービスに接続したら、次のコード行 (とあの見た目に複雑そうな Associators Of クエリ) を使用して、C:\TemperatureData フォルダにあるすべてのファイルのコレクションを返します。
Set colFiles = objWMIService.ExecQuery _
("ASSOCIATORS OF {Win32_Directory.Name='C:\TemperatureData'} Where " _
& "ResultClass = CIM_DataFile")
これでようやく、本題の処理を行う準備が整いました。
注 : ええ、スクリプト側では本題の処理を行う準備が整いました。しかし、だからと言って Scripting Guys の方でも準備が整っているとは限りません。少なくとも、Scripting Guys のうちの 1 人は準備が整っていないでしょう。 |
ここでは、まず C:\TemperatureData にあるファイルのコレクションをループ処理する For Each ループを設定する必要があります。このループ内ではどんな処理を行うのか、ですって。まずは、次のような処理を行います。
strFileName = objFile.FileName strFilePath = objFile.Name
1 行目では、FileName プロパティの値を strFileName 変数に代入します。WMI では、FileName プロパティの値は、単なるファイル名で、ファイル拡張子は含まれていません。たとえば、C:\TemperatureData\Apr_avg_temp.txt というファイルがあるとすると、このファイルの FileName プロパティの値は Apr_avg_temp です。
2 行目では、Name プロパティの値を strFilePath 変数に代入します。この Name プロパティという名前は、実は少し誤解を招くおそれがあります。というのも、WMI では、ファイルの Name プロパティの値はファイルのパスに相当するからです。つまり、C:\TemperatureData\Apr_avg_temp.txt ファイルの Name プロパティの値は、C:\TemperatureData\Apr_avg_temp.txt になります。
2 つの変数に値を代入したら、OpenTextFile メソッドを呼び出してコレクション内の 1 つ目のファイルを開きます。その処理を行っているのが、次のコード行です。
Set objFile = objFSo.OpenTextFile(strFilePath, ForReading)
ファイルを開いたら、ReadAll メソッドを使用してファイルの内容全体を読み取り、その内容を strContents という適切な名前が付けられた変数に格納します (ファイルの内容全体を読み取って変数に代入する理由については、この後すぐにご説明します)。その後、Close メソッドを呼び出し、ファイルを閉じます。
またまたいい質問ですね。まだファイル名をファイルの 1 行目のデータとして挿入する必要があるのに、なぜファイルを閉じるのか、というご質問ですね。
ええ、まだその処理を行う必要があります。ただし、最初は読み取り用にファイルを開きました。ファイルから内容を読み取るためには、ファイルを読み取り用に開く必要があります。FileSystemObject を使用すると、読み取り用または書き込み用のどちらかのモードでファイルを開くことはできますが、両方の処理を行えるモードでファイルを開くことはできません。ですから、読み取り用にファイルを開き、ファイルから内容を読み取って、ファイルを閉じる必要があります。その後、今度は書き込み用としてファイルを開きます。
それが、偶然にも、次のコード行で行われている処理です。
Set objFile = objFSo.OpenTextFile(strFilePath, ForWriting)
では、次の行に移りましょう。
strContents = strFileName & vbCrLf & strContents
既に確認したように、FileSystemObject は少し扱いにくいところがあります。何しろ、読み取りと書き込みの両方の処理を行えるモードでファイルを開くことができません。そのうえ、ファイルを選択して変更することもできません。ファイルに変更を加える唯一の方法は、ファイルの内容をメモリに読み込み、メモリ内で変更を加え、その後ファイル全体を書き換えるという方法です。これでファイルの内容全体をメモリに読み込む理由がおわかりいただけたと思いますが、メモリですべての変更を行う必要があるからです。
これでつい先ほどお見せしたコード行についても理解していただけると思います。コード行では、次の項目を組み合わせて新しいファイルを構成しています。
| • | ファイルの名前 (strFileName 変数に格納されています) |
| • | 復帰改行 (VBScript の vbCrLf 定数によって表されています) |
| • | ファイルの既存の内容 (strContents 変数に格納されています) |
これらの項目を連結すると、strContents 変数の値は、次のようになります。
apr_avg_temp 45.544 34.544 65.433 56.783 32.451 65.432 22.435. 44.564
もうおわかりだと思いますが、これは "まさに" 望みどおりの結果ですよね。これは好都合です。というのも、ここで行う必要があるのは Write メソッドを呼び出して、1 つ目のファイルの既存の内容を、現在メモリに格納されている変更した内容と置き換える処理だけだからです。
objFile.Write strContents
その後、ファイルを閉じて、ループ処理の先頭に戻り、コレクション内の次のファイルに対して同じ処理を繰り返します。
DJ さん、これでうまくいくでしょう。このコラムを執筆している Scripting Guy は、自分のことをあまり哀れだと思っていません。物事は遅かれ早かれ必ず好転し始めるものです。少なくとも、Scripting Guys は木曜日の朝にバルセロナに向けて旅立ち、TechEd IT Forum に参加します。ええと、空港で安全検査に対処し、機内食を堪能し、飛行機に 4 時間半乗ってアトランタに移動し、アトランタ空港で 3 時間ぼんやりと過ごし、飛行機に 9 時間乗って大西洋を横断します。今日よりも "はるかに" 良い日になりそうです。また明日お会いしましょう。