JScriptバッチで,外部のスクリプトをロード+再利用する方法 (WSFで import / include する)
WSHでプログラムの「再利用」をしたい場合,下記の3つの方法がある。
- 単にコピペする。 ←面倒
- 文字列として読み込んで eval() する。 ←制限がある※後述
- WSF形式にする。 ←これがベスト ※後述
WSHと同じくWSF(Windows Script File)形式のバッチも,ダブルクリックするだけで実行できる。
しかし,なぜ eval() を使う方法ではだめなのか。
バッチをライブラリ化したい
自分が作った .js ファイルを,次からWSHのライブラリとして使い回せたら便利だ。
JScript.NET だったら import で名前空間を作れるのだが,WSHにはそういった文が無い。
JScript.NET 名前空間とライブラリ
http://homepage3.nifty.com/aya_js/JSc...
このため,「外部の js ファイルを文字列として読み込み,eval() で評価する」という対処法が利用される。
JScriptでファイルのインクルードを行う
http://maglog.jp/pueblo-del-script/Ar...WSHで外部スクリプトのロード
http://dara-j.asablo.jp/blog/2007/04/...
いいアイデアなのだが,この方法の問題点は2つある。
(1)eval() ではファイル操作できない
下記のコードを 適当な名前.js で保存し,ダブルクリックしてみよう。
// evalを使わない var fso = WScript.CreateObject('Scripting.FileSystemObject'); fso.CreateTextFile('a.txt'); var txt = fso.OpenTextFile('a.txt',2); txt.WriteLine('hoge'); txt.Close();
普通に,カレントディレクトリに a.txt ができて「hoge」と書き込まれる。
では,下記のようにしたらどうなるか?
// evalを使う var str = "var fso = WScript.CreateObject('Scripting.FileSystemObject');" + "fso.CreateTextFile('b.txt');" + "var txt = fso.OpenTextFile('b.txt',2);" + "txt.WriteLine('hoge');" + "txt.Close();" eval( str );
エラー:書き込みできません。 コード:800A0046 ソース:Microsoft JScript 実行時エラー
というエラーになる。
なぜかテキストファイルは生成されているが,中身は空。
文字通り,書き込みができていないのだ。
「書き込みできません」のエラーは,一般には,WSHのファイル操作で無茶した場合に出る。
開発リソース/JScript/FileSystemObjectの落とし穴
http://www.isla-plata.org/wiki/pukiwi...
ここでは,JScriptにおける eval() の仕様が関係している。
eval メソッドは制限付きのセキュリティ コンテキストでスクリプトを実行します
http://msdn.microsoft.com/ja-jp/libra...
ユーザーを保護するため、eval メソッドに渡されるコードは、文字列 "unsafe" が 2 番目のパラメータとして渡されていない限り、制限付きのセキュリティコンテキストで実行されます。
制限付きのセキュリティ コンテキストでは、ファイル システム、ネットワーク、ユーザーインターフェイスなどのシステム リソースへのアクセスが禁止されます。これらのリソースにアクセスしようとすると、セキュリティ例外が生成されます。
eval の 2 番目のパラメータが文字列 "unsafe" である場合、eval メソッドに渡されるコードは、呼び出し元のコードと同じセキュリティ コンテキストで実行されます。これにより、eval メソッドの以前の動作が復元されます。
セキュリティ上問題のあるコードはevalされないらしい。
JScript.NETでは,evalの第二引数に "unsafe" を追加することによってその制限を解除できるようだが,WSHでは不可能のようだ。
JScript.NET で "unsafe" の例
http://miau.s9.xrea.com/blog/index.ph...
つまり,eval() で外部スクリプトを擬似 import しても,ファイル操作系のコードは動かないのだ。
これは「ファイル操作系のライブラリを作れない」事を意味する。それでは困る。
と見せかけて1つある。
※09.2.4.修正・・・・・・
evalとTextStream.OpenTextFileのナゾ
http://dara-j.asablo.jp/blog/2009/01/...
(2)読み込んでも,使えるのは eval() したスコープだけ
かりに,インポートの関数を作ったとしよう。
a.js
// 外部スクリプトを読み込む関数 function import_js( js_path ) { eval( WScript.CreateObject("Scripting.FileSystemObject") .OpenTextFile( js_path, 1 ) .ReadAll() ); } // 読み込み import_js( "b.js" ); // 読み込んだライブラリの内容を実行 var ht = new HelloTalker(); ht.hello();
b.js
// hello と表示するクラス var HelloTalker = function(){}; HelloTalker.prototype = { hello : function(){ WScript.Echo("hello"); } };
a.js内にb.jsをインポートして実行しているつもりだが,これは動かない。
外部スクリプト中で var で宣言されている変数は,import_js 関数の外側に出られない。
a.jsは修正される。
// 外部スクリプトを読み込む eval( WScript.CreateObject("Scripting.FileSystemObject") .OpenTextFile( b.js, 1 ) .ReadAll() ); // 読み込んだライブラリの内容を実行 var ht = new HelloTalker(); ht.hello();
これなら動くが,読み込み部分が冗長になる。
また前述のとおり,これでもファイルアクセス系の処理はeval()できない。
wsfでこれらを解決する。
WSFを使おう
HTMLでJavaScriptファイルをロードしたい場合は,scriptタグを使って簡単に .js の外部読み込みができた。
wsfなら,それと同じ事ができる。
WSFは,WSHのコードを
- jobタグ
- scriptタグ
で囲っただけ。
下記の内容を hello.wsf で保存し,ダブルクリックする。
<job> <script language="JavaScript"> WScript.Echo("hello"); </script> </job>
JScriptファイルの時と同様,「hello」と表示される。
これを使って,前述の a.js を書き変えてみる。
a.wsf
<job> <script language="JavaScript" src="b.js"></script> <script language="JavaScript"> // 読み込んだライブラリの内容を実行 var ht = new HelloTalker(); ht.hello(); </script> </job>
src=〜の部分で外部スクリプトを include している。
ファイル操作系のコードも読み込める。
ダブルクリックすると,helloと表示される。
まとめ
ここまで考慮すると,JScriptで手早くバッチを作る際の方針ができる。
- 機能を提供するライブラリは「.js 」で作成する。
- 直接実行しない。
- 中身は,その機能を実装したクラスやオブジェクト。
- そのクラスの利用コードのサンプルを,ファイル中でコメントアウトで示しておくと便利。
- 機能を利用するスクリプトは「.wsf」で作成する。
- src=〜でライブラリを読み込む。
- ダブルクリックで実行する。
もちろんVBScriptにも当てはまる。
WSFの詳しい書き方については,下記ページ等を参照。
プログラムの再利用方法
http://www.happy2-island.com/vbs/cafe...