WSHとは 正式名称 Windows Scripting Hostを 略した名称で、Microsoft Windowsにおいてテキストファイルに記述したスクリプトを実行するスクリプト実行環境です。標準では、MicrosoftのVBScriptとJScript言語を利用できます。また、WSHは内部でActiveXを使っているためにWindowsが持つたくさんのサービスが呼び出せ、かなり強力な機能を持ってます。
以下のページを見る前に、MicrosoftのWindows ScriptやWindows Script Host Laboratoryのサイトを一読することをお勧めします。また、ここからWSHのオンラインヘルプをダウンロードしておくと便利です
OgOが装備するJavaScript言語では、スクリプトの最初に
var {WScript} = require("wsh");
を実行すれば、強力な機能を持つ WSHが使えるようになります。
さらにActiveXの機能を使いたい場合、
var {ActiveX} = require("ole");
も実行してください。
以下に、サンプルをとうしてWSHの使い方を説明します。
まず次のようなテキストファイルを作ります。
start.js
var {WScript} = require("wsh"); WScript.Echo("こんにちは");
拡張子は .js とします。このスクリプトを実行するには、2つの方法があります。
var {WScript} = require("wsh"); var v = new vector(0, 1, 2); WScript.Echo(v); //(0, 1, 2) //vectorオブジェクト function vector(x, y, z) { this.x = x; this.y = y; this.z = z; this.toString = function () { return "(" + this.x + ", " + this.y + ", " + this.z + ")"; }; }
var {WScript} = require("wsh"); //Numberオブジェクト var a = 1e-7 WScript.Echo(a); //1e-7 //VBのDate型オブジェクト var fs = WScript.CreateObject("Scripting.FileSystemObject"); var WShell = WScript.CreateObject("Wscript.Shell"); var dsktop = fs.GetFolder(WShell.SpecialFolders("Desktop")); WScript.Echo(dsktop.DateLastModified); //Thu Mar 03 2011 09:05:55 GMT+0900 //Booleanオブジェクト var b = true; WScript.Echo(b); //true
OgOx では次のように引数をとることができます。
OgOx mtable.js 6 7 > mtable.html [Enter]
ここでは、6 と 7 が引数になっています。 リダイレクトで標準出力がmtable.htmlに書き出されます。
var {WScript} = require("wsh"); //かけ算の表を作るプログラム var r, c, i, j; var args = WScript.Arguments; //引数の処理(かなり甘い) if(args.length != 2) //引数の数が2でないなら QuitForError(); if((r = parseInt(args[0])) <= 0) QuitForError(); if((c = parseInt(args[1])) <= 0) QuitForError(); //表を作る WScript.Echo("<HTML>\n<BODY STYLE=\"text-align:center;\">"); WScript.Echo("<TABLE BORDER>"); for(i = 1; i <= r; i++) { WScript.Echo("<TR>"); for(j = 1; j <= c; j++) WScript.Echo("<TD>" + i * j + "</TD>"); WScript.Echo("</TR>"); } WScript.Echo("</TABLE>\n</BODY>\n</HTML>"); function QuitForError() { WScript.Echo( "usage : OgOx mtable.js nrows ncolumns"); WScript.Quit(); //プログラムを終了する }
このプログラムを実行すると、次のようなHTMLファイルが生成されます。
プログラムは、1番目の引数がrに入り、2番目の引数がcに入って、 r行×c列のかけ算の表を作ります。
WScript.Argumentsは 配列を返します。args.lengthで引数の個数を返します。 上の例では2を返します。args[0]で最初の引数を返します。 args[1]で2番目の引数を返します。
このようにWSHで、使えるプログラムを組むことができます。
以下では、本当のWSHの価値を発見できるサンプルを掲載しています。
これらを参考にして普段のデータ処理を自動化してみてはいかがでしょうか。
FileSystemObjectを使うと、ファイルシステムを操作することができます。
FileSystemObjectを得るにはつぎのようにします。
var fs = WScript.CreateObject("Scripting.FileSystemObject");
FileSystemObjectを使う際に必要な定数はつぎのようにして取得できます。
var FS = WScript.loadConsts(fs); // FS.Normal==0, FS.ReadOnly==1, ...
テキストファイルの読み書きをするにはまず、 TextStreamを得る必要があります。
ファイルを読み取りまたは追加書き込みする場合は、
var stream = fs.OpenTextFile(filename, iomode);
とします。追加書き込みの場合、iomodeを Fs.ForAppending (8)とします。読み取りの場合省略できます。
読み取りは典型的に次のように行います。
score.csv
名前,性,国語:Int,算数:Int,理科:Int,社会:Int さくら,女,75,71,8,66 知世,女,62,2,89,60 小狼,男,51,28,70,31 千春,女,68,61,46,12 奈緒子,女,89,55,69,24 利佳,女,79,30,79,81
このようなテキストファイルを読んで、各人の合計の点数を表示します。
sum.js
var {WScript} = require("wsh"); var str, arr, i, sum; var fs = WScript.CreateObject("Scripting.FileSystemObject"); var stream = fs.OpenTextFile("score.csv"); //読み取り専用 stream.SkipLine(); //一行読み飛ばし while(!stream.AtEndOfStream) { //ファイルの終端に達するまで str = stream.ReadLine(); //一行読む arr = str.split(","); sum = 0; for(i = 2; i < arr.length; i++) sum += parseInt(arr[i]); //得点を足していく WScript.Echo(arr[0] + " " + sum); //名前と総得点を表示 } stream.Close(); //最後にファイルをクローズ
OpenTextFileメソッドでテキストファイルを開いた後、 ReadLineメソッドで一行ずつ読みこみます(改行文字は省略されて値が返されます)。 AtEndOfStreamプロパティがtrue になったらファイルの終端まで達したことになるので Closeメソッドでファイルを閉じます。
書きこむ方法はほかに、指定した文字数を読みこむRead メソッド、ファイルを丸ごと読みこむReadAll メソッドがあります。
新規にテキストファイルを作成する場合は、
var stream = fs.CreateTextFile(filename, isoverwrite);
とします。isoverwriteがtrueでなく falseの場合、 filenameで指定されるファイルが存在するとエラーが発生します。
前回と同じかけ算の表を作ってみましょう。
var {WScript} = require("wsh"); //かけ算の表を作るプログラム 2 var r, c, i, j; //今回は引数の処理は無し r = 6; c = 7; var fs = WScript.CreateObject("Scripting.FileSystemObject"); var stream = fs.CreateTextFile("mtable2.htm"); //表を作る with(stream) { Write("<HTML>\n<BODY STYLE=\"text-align:center;\">\n"); Write("<TABLE BORDER>\n"); for(i = 1; i <= r; i++) { Write("<TR>"); for(j = 1; j <= c; j++) Write("<TD>" + i * j + "</TD>"); WriteLine("</TR>"); } Write("</TABLE>\n</BODY>\n</HTML>\n"); Close(); }
CreateTextFileで新規ファイルを開いた後、 WriteあるいはWriteLine メソッドでテキストを書きこみます。WriteLineメソッドは最後に改行文字を書きこみます。最後にCloseメソッドでファイルを閉じます。
FileSystemObjectを使って、 ドライブ・フォルダ・ファイルのそれぞれのオブジェクトを得ることができます。
var {WScript} = require("wsh"); var fs = WScript.CreateObject("Scripting.FileSystemObject"); var dr = fs.GetDrive("c:"); var fo = fs.GetFolder("c:\\windows"); var fi = fs.GetFile("c:\\command.com");
GetDriveの引数は、"c"、"c:\\"でもOKです。 GetFolderの引数は、"c:\\windows\\"でもOKです。
ドライブなどが存在しない場合はエラーが発生します。
これらのオブジェクトのコレクションを それぞれの上位階層のオブジェクトなどから得ることもできます。
drc = fs.Drives foc = fo.SubFolders fic = fo.Files
これらのコレクションの要素は 次のように Enumeratorを用いて順に取り出すことができます。
var {WScript, Enumerator} = require("wsh"); var fs = WScript.CreateObject("Scripting.FileSystemObject"); var f = fs.GetFolder(".") //すべてのサブフォルダを表示 for (var subfc = new Enumerator(f.SubFolders); !subfc.atEnd(); subfc.moveNext()) { WScript.Echo("[" + subfc.item().Name + "]"); } //すべてのファイルを表示 for (var fc = new Enumerator(f.Files); !fc.atEnd(); fc.moveNext()) { WScript.Echo(fc.item().Name); }
このスクリプトはDOSのコマンドDIRと同様に、 そのフォルダ内のフォルダとファイルを一覧表示します。
for (var fc = new Enumerator(f.Files); !fc.atEnd(); fc.moveNext()) { fc.item() ……; }
で、f.Filesで得られるフォルダ内のすべてのファイルの集まり Filesコレクションの要素のFileオブジェクトが 次々にf1で参照されるようになり処理されます。 その上も同様でそのフォルダ内のすべてのサブフォルダについて処理されます。
サブルーチンを再帰的に呼び出すことにより、 フォルダの中をすべてスキャンするプログラムがよく使われます。
var {WScript, Enumerator} = require("wsh"); var fs = WScript.CreateObject("Scripting.FileSystemObject") // 引数が無ければカレントディレクトリ、 // あれば第1引数からスキャンする var objArgs = WScript.Arguments; var str; if (objArgs.length == 0) { str = "."; }else{ str = objArgs[0]; } var f = fs.GetFolder(str); ScanFolder(f); // フォルダ内の全てのフォルダについて処理する function ScanFolder(f) { for (var subf=new Enumerator(f.SubFolders); !subf.atEnd(); subf.moveNext()) { ScanFolder(subf.item()); } WScript.Echo(f.Path); }
フォルダとファイルのコピーは次のようにします。
var {WScript} = require("wsh"); var fs = WScript.CreateObject("Scripting.FileSystemObject"); var f = fs.GetFolder("scripts"); f.Copy("c:\\backup\\", true); var f1 = fs.GetFile("aname.pl"); f1.Copy("c:\\backup\\", true);
Copyメソッドの1番目の引数はコピー先、 2番目は省略可能で、上書きするかどうかです。 デフォルトは true です。 false で上書きしようとするとエラーになります。
フォルダの場合は、下の階層のファイル・フォルダも全てコピーします。
フォルダとファイルの移動も同様です。
f.Move("c:\\backup\\"); f1.Move("c:\\backup\\");
フォルダとファイルの削除は次のように行います。
f.Delete(true); f1.Delete(true);
引数は読取専用のファイル・フォルダも強制的に削除するかどうかです。 デフォルトは false です。 false で読取専用のファイル・フォルダを削除しようとするとエラーが出ます。
コピー・移動の操作はFileSystemObject を用いたほうが簡単かもしれません。
fs.CopyFolder("scripts", "c:\\backup\\", true); fs.CopyFile("aname.pl", "c:\\backup\\", true);
1番目の引数はフォルダ・ファイルを表す文字列、2番目はコピー先、 3番目は上書きするかどうかです。
1番目の引数には次のようにワイルドカードを用いることも可能です。
fs.CopyFolder("???????", "c:\\backup\\", true); fs.CopyFile("*.pl", "c:\\backup\\", true);
移動も同様です。
fs.MoveFolder("scripts", "c:\\backup\\"); fs.MoveFile("aname.pl", "c:\\backup\\");
Name | ファイルの名前 |
ShortName | 8.3形式のファイル名 |
Path | ファイルのフルパス |
ShortPath | 8.3形式のフルパス |
Size | バイト単位のファイルサイズ |
Type | ファイルの種類 ex) "GIF イメージ" |
Attributes | ファイルの属性 |
DateCreated | 作成日時 |
DateLastModified | 更新日時 |
DateLastAccessed | 最終アクセス日時 |
Drive | ファイルが格納されているDriveオブジェクト |
ParentFolder | ファイルが格納されているFolderオブジェクト |
Name | フォルダの名前 |
ShortName | 8.3形式のフォルダ名 |
Path | フォルダのフルパス |
ShortPath | 8.3形式のフルパス |
Size | バイト単位のフォルダサイズ |
Attributes | フォルダの属性 |
DateCreated | 作成日時 |
DateLastModified | 更新日時 |
DateLastAccessed | 最終アクセス日時 |
Drive | フォルダが格納されているDriveオブジェクト |
ParentFolder | フォルダが格納されているFolderオブジェクト |
IsRootFolder | ルートフォルダかどうかを返す |
Files | フォルダ内のFileオブジェクトのコレクション |
SubFolders | フォルダ内のFolderオブジェクトのコレクション |
TotalSize | バイト単位の総ディスク容量 |
FreeSpace | バイト単位の使用可能なディスク容量 |
AvailableSpace | バイト単位のユーザが使用可能なディスク容量 |
DriveLetter | ドライブ名 ex) "C" |
Path | パスを返す ex) "C:" |
VolumeName | ボリューム名 |
ShareName | ドライブのネットワーク共有名 |
RootFolder | ルートフォルダを表すFolderオブジェクト |
DriveType | ドライブの種類を示す値を整数で返す" |
FileSystem | ファイルシステムを表す文字列 ex) "FAT" |
SerialNumber | ディスクボリュームのシリアル番号 |
IsReady | 準備できているかどうかを返す |
ファイル・フォルダの属性は次の値の加算によって表されます。
Normal | 0 | 通常のファイル |
ReadOnly | 1 | 読み取り専用ファイル |
Hidden | 2 | 隠しファイル |
System | 4 | システムファイル |
Volume | 8 | ディスク ドライブ ボリューム ラベル |
Directory | 16 | フォルダ |
Archive | 32 | ファイルが前回のバックアップ以降に変更されているかどうか |
Alias | 1024 | ショートカット |
Compressed | 2048 | 圧縮ファイル |
例えば、読み取り専用で隠しファイルなら3が返ります。そのファイルが隠しファイルかどうかを判定するには次のようにします。
if(f1.Attributes & FS.Hidden) WScript.Echo(f1.Name + "は隠しファイル"); else WScript.Echo(f1.Name + "は隠しファイルでない");
DriveTypeプロパティは整数を返しますが、それぞれの値は次のドライブの値と対応しているようです。
UnknownType | 0 | 不明 |
Removable | 1 | リムーバブル ディスク |
Fixed | 2 | ハード ディスク |
Remote | 3 | ネットワーク ドライブ |
CDRom | 4 | CD-ROM |
RamDisk | 5 | RAM ディスク |
各オブジェクトの日時を返すプロパティは、Yearは1900年からの差分年数となります。
正しい西暦年数は、getFullYearメソッドを使ってください。
var {WScript} = require("wsh"); var fs = WScript.CreateObject("Scripting.FileSystemObject"); var windir = fs.GetFolder("c:\\windows"); var d = windir.DateLastModified; WScript.Echo(d.getYear()); //1900年からの差分年数 WScript.Echo(d.getFullYear()); //西暦年数
今回はExcelを操作する方法についてみていきます。これは、VBAをふだんから使っている人にとっては非常に簡単です。
最初に、ExcelのApplicationオブジェクトにあたるオブジェクトを得ます。
var excel = WScript.CreateObject("Excel.Application");
それから、とりあえずExcelを見えるようにしておきます。
excel.Visible = true;
そのあとは、VBAでApplicationとしているところ、またはそれが省略されているところでは Applicationオブジェクトを補っておきます。例えば、
excel.Cells(1, 1).Value = 1;
だいたいこれくらい分かっておけば書けるでしょう。下の例は、モンテカルロ法で円周率を求めるプログラムです。座標値が0~1を取る正方形の中に疑似乱数で点を打ち、その点が中心が原点、半径1の円内に入るかどうかを調べます。 1、2列目がx、yの値、3列目が円の中に入っていれば1、入っていなければ0になります。この3列目の平均を取って4倍した値が概算の円周率です。
var {WScript} = require("wsh"); var i, rmax; const pi = Math.PI; var excel = WScript.CreateObject("Excel.Application"); const XL = WScript.loadConsts(excel); // Excelの定数 excel.Visible = true; excel.WorkBooks.Add(); //新規ブック // 1/4円を描くためのデータ 4列目がx、5列目がy //本当はワークシート関数を使ったほうが速い for(i = 0; i <= 50; i++) { excel.Cells(i + 1, 4).Value = Math.sin(pi * i / 100); excel.Cells(i + 1, 5).Value = Math.cos(pi * i / 100); } //座標値が0~1の値を取る2次元の点が //半径1の円の中にいくつ入ったかで円周率を求める rmax = 1000; with(excel) { Range(Cells(1, 3), Cells(rmax, 3)).FormulaR1C1 = "=if(rc1*rc1+rc2*rc2<1,1,0)"; Range(Cells(1, 1), Cells(rmax, 2)).FormulaR1C1 = "=rand()"; } excel.Cells(1, 7).FormulaR1C1 //結果 = "=average(r1c3:r" + rmax + "c3)*4"; excel.Cells(1, 7).Borders.Weight = XL.xlMedium; //疑似乱数によって生成された点と1/4円をグラフとして描く with(excel) { ActiveSheet.ChartObjects().Add(60, 30, 330, 300).Select(); //以下略 }
気をつけなければならないのは、Rangeメソッドです。
//エラー excel.Range(Cells(1, 1), Cells(3, 1)).Value = 3; //正常動作 excel.Range(excel.Cells(1, 1), excel.Cells(3, 1)).Value = 3; //正常動作 (with文で excel. を省略) with(excel) { Range(Cells(1, 1), Cells(3, 1)).Value = 3; }
名前付き引数は { 引数名 : 値, ... } で与えます。
transpose.js
var {WScript} = require("wsh"); var excel = WScript.CreateObject("Excel.Application"); excel.Visible = true; excel.WorkBooks.Add(); with (excel) { Cells(1, 1).Value = 1; Cells(2, 1).Value = 2; Range("A1:A2").Copy(); Range("C1").PasteSpecial( {Transpose:true} ); //Transpose:True 貼り付けるときにセル範囲の行と列を入れ替えます。 Application.CutCopyMode = false; //コピーした範囲が点線で点滅するのを止めます。 }
下は1日の残業時間が書かれたテキストファイルを読み込んで、 Excelに書き出すスクリプトです。
D:\work\残業TXT の下に毎朝、前日の残業時間が書かれたテキストファイルがいつも置かれているとします。
残業110311.txt
さくら,知世,小狼,千春,奈緒子,利佳 0, 0, 2, 0.5, 1, 0.5
D:\work\残業XLS の下に毎月の残業時間がまとめられたブックがあるとします。
さくら 知世 小狼 千春 奈緒子 利佳 3/1/2011 1 1 0 1.5 1 2 3/2/2011 0.5 0.5 0 0.5 1 2
そして、タスクスケジューラで毎朝10時にスクリプトが動くなどと設定しておけば、自動的にテキストファイルを読み込んで、その月のブックのその日の行に各人の残業時間が書かれます。その月のブックがなければ前月分などから新しく作ります。Excelはウィンドウを見せないようにしていて、スクリプトが終わる前にExcelを終了するようにしているので、画面上には何も現れず、HDDが静かなマシンであれば動作していることに気づかないかもしれません。
var {WScript, Enumerator} = require("wsh"); var fs, source_fld, excel_fld, stream, i, str; fs = WScript.CreateObject("Scripting.FileSystemObject"); source_fld = fs.GetFolder("d:\\work\\残業TXT"); //テキストファイルから各人の残業時間を読み取る var fc = new Enumerator(source_fld.Files); var arrData = new Array(); var nData = 0; for(; !fc.atEnd(); fc.moveNext()) { //ファイル名がパターンにマッチするテキストファイルを読む if(/残業[0-9]+\.txt/.test(fc.item().Name)) { arrData[nData] = new Data(); //ファイル名から日付を得る arrData[nData].setDate(fc.item().Name); stream = fs.OpenTextFile(fc.item()); //1行目は名前の配列 str = stream.ReadLine(); arrName = str.split(","); //2行目は残業時間の配列 str = stream.ReadLine(); arrHours = str.split(","); stream.Close(); for(i = 0; i < arrName.length; i++) arrData[nData].setData(arrName[i].trim(), arrHours[i]); nData++; } } var r,c; //データをブックに書きこむ var excel = WScript.CreateObject("Excel.Application"); var XL = WScript.loadConsts(excel); //objXL.Visible = true; ウィンドウを見せない excel_fld = fs.GetFolder("d:\\work\\残業XLS"); for(i = 0; i < nData; i++) { with(excel) { OpenMonthBook(arrData[i].d); r = arrData[i].d.getDate() + 1; //書きこむ行 str = arrData[i].d.toLocaleString(); Cells(r, 1).Value = str.substring(0, 10); //日付 cmax = Cells(1, 2).SpecialCells(XL.xlLastCell).Column; for(c = 2; c <= cmax; c++) { str = Cells(1, c).Text.trim(); //名前 if(arrData[i].IsExist(str)) Cells(r, c).Value = arrData[i].getZangyou(str); } ActiveWorkbook.Save(); } } excel.Quit(); //その月に対応するブックを開く function OpenMonthBook(d) { //省略 } //1日のデータを表すオブジェクトの定義 function Data() { this.d; this.dic = {}; this.setDate = setDate; this.setData = setData; this.IsExist = IsExist; this.getZangyou = getZangyou; } //以下略
Excelだけ使っているとあまり使うことはないかもしれませんが、 WSHではウィンドウのサイズ、スタイルを変えるプロパティが有用です。
var {WScript} = require("wsh"); var excel = WScript.CreateObject("Excel.Application"); var XL = WScript.loadConsts(excel); //定数 excel.Visible = true; excel.Left = 0; //左端の座標 excel.Top = 0; //上端の座標 excel.Width = 600; //幅 excel.Height = 500; //高さ excel.WindowState = XL.xlMaximized; //最大化 excel.WindowState = XL.xlNormal; //元の大きさに excel.WindowState = XL.xlMinimized; //最小化 excel.Workbooks.Add();
今回はInternet Explorerを操作する方法についてみていきます。 Internet Explorerを操作できると色々なオートパイロットができます。 オートパイロットのソフトには色々ありますが、 機能が充実していなかったり、シェアウェアだったりします。 一歩先を行くユーザは自由にカスタマイズできるようにWSHで書きましょう。
「Windows JavaScript」-「WebFormクラス」を使えば、Internet Explorerをよりカスタマイズした扱いができます。
最初に、Internet Explorerオブジェクトを得ます。
var IE = WScript.CreateObject("InternetExplorer.Application");
それから、IEを見えるようにしておきます。
IE.Visible = true;
次のスクリプトは私がいつもデスクトップに置いて使っているものです。
var {WScript} = require("wsh"); var IE = WScript.CreateObject("InternetExplorer.Application"); IE.Visible = true; IE.Width = 680; IE.Height = 660; IE.GoHome();
IEを立ち上げるとたぶん、ウィンドウが横長になってしまうと思います。 私はこれが気に入らないので、開いた後Widthと Heightで横と縦の長さを変えています。 これ時点ではページが表示されていないので、最後にホームを表示します。私の作っているページは最近はたいていこの680という幅を前提にしています。 これより狭いと若干見にくくなるかもしれません。
IE.Document
とすると、ページ上のJavaScriptでいつも使っている documentオブジェクトを得ることができます。 これさえ得られれば、JavaScriptをふだんから使っている人なら あとは思いのままでしょう。例えば、下のボタンを押すスクリプトを考えましょう。