スポンサーリンク

JScriptバッチで,外部のスクリプトをロード+再利用する方法 (WSFで import / include する)


WSHでプログラムの「再利用」をしたい場合,下記の3つの方法がある。

  1. 単にコピペする。  ←面倒
  2. 文字列として読み込んで eval() する。  ←制限がある※後述
  3. 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...

wsfとは
http://d.hatena.ne.jp/chaichanPaPa/20...