JavaScript
WSH
JScript

結局、Perl の qx(`STRING`)的なものになった。

More than 1 year has passed since last update.

Windows環境で、WSH/JScript をバッチファイル的に使い倒してる。

今更なんで PowerShell じゃなくWSHかというと、少なくとも現状WordやExcelをブン回そうとしたら、PowerShell よりもWSHの方が扱いやすいから。

なんでVBScriptじゃなくJScriptかというと、単にVB系が嫌いだから。というだけでなくJavaScriptであるところのJScriptは、仕様のあちこちにPerlの匂いがプンプンするから。

Perlで配列処理ばんばんやってたので、JavaScriptのArray系のメソッドはもう見る前からどんなものがあるか見当がついちゃう状態。(Stringにtrは無かったけどさ。)

正規表現もRegExpのインスタンス起こしたりとかめんどくせーなーと思ってたら、JavaScriptには正規表現リテラルなんてすんばらしいものがあるし。

VBScriptにはClassがあるけどJScriptには無いところだけは負けてるなー。と最初は思っていたけど、勉強していくうちにクラスとは違う考え方の、プロトタイプ・ベースのオブジェクト指向という別の文化なので、別に負けてるわけではないし。

Windows環境で、WSH/JScript をバッチファイル的に使う場合に重要なのは、WSH固有のオブジェクト群。WScript オブジェクト、WshShell オブジェクトあたりは頻用・重要。あとScripting ランタイムライブラリのFileSystemObject。これらを手のうちに入れられれば、Windows環境でかなりのことをバッチファイルよりも効率よく書けるようになる。

WSH/JScript から別のプログラムを利用するには、①ActiveXObject対応だったらインスタンス化して直接操作する。②それ以外のものなら子プロセスとして起動して使う。のどっちかになると思う。そのために使う手段としては、WshShell オブジェクトのRun メソッドかExec メソッドの2つがある。

Run メソッドは「新しいプロセス内でプログラムを実行します。」。ウィンドウの外観をコントロールしたり、プロセスが終了するのを待たずに先に進んだり出来る。もちろん終了を待つこともできる。

Exec メソッドは「子コマンドシェルでアプリケーションを実行します。アプリケーションから StdIn/StdOut/StdErr ストリームにアクセスできます。」。

これがどちらも一長一短で。

・Run メソッド:標準入出力を扱えない。

・Exec メソッド:完了コードを取得できない。ウィンドウを非表示に出来ず黒い画面が出てしまう。

というわけで、Run メソッドを核に、標準入出力や標準エラー出力を扱えるような機能を備えたモジュールを作ろうとしてたが、あれこれやりすぎて仕様がこんがらがって、自分でもよくわからなくなってきたので、そちらは一度ストップした。実際に自分で使おうとしても使い方がよくわからなかったりして。自作のモジュールだというのになんてこったと。

自作モジュールを使うためにあれこれやってる最中、「あーこんなもんPerlなら$foo = `dir /b`でおしまいなのに…」なんてことを思った途端、「じゃあそれを作ればいいじゃないの。」とひらめいたので、改めてそっちを作ることにした。

標準入力はともかく、標準エラー出力や完了コードが欲しいこともあるよな。ってことで、拡張モードは付けた。てなわけで出来たのがこちらでございます。


perlqx.js

// magic spell for WSH //////////////////////////////////////////////////////

if(typeof console==="undefined")console={log: function(s){WScript.Echo(s)}};
if(typeof process==="undefined"){process={exit:function(e){WScript.Quit(e)}};
var v=process.argv=[]; v[0]=WScript.FullName; v[1]=WScript.ScriptFullName;var
n=WScript.Arguments.Unnamed; for(var i=0; i<n.length; i++)v.push(n.item(i))};
/////////////////////////////////////////////////////////////////////////////

// もうシンプルに perl の qx(`STRING`)相当の機能に絞る。

// カレント・ディレクトリの拡張子 .js のフルパスリストを取得
var cwd = chomp( perlqx("cd") );
var dirs = perlqx("dir /b" );
dirs = dirs.split(getEOL(dirs)); // 甘く見てsplit("\n")とかやってハマった。

var r = [];
for( var k in dirs ) if( /\.js$/i.test(dirs[k]) ) r.push(cwd + "\x5C\x5C" + dirs[k]);

console.log( r.join("\n") );

/////////////////////////////////////////////////////////////////////////////

function getEOL(str){ // 安直に行末コードを判定する
if(str.indexOf("\r\n")!=-1) return "\r\n";
if(str.indexOf("\n")!=-1) return "\n";
if(str.indexOf("\r")!=-1) return "\r";
return; // 不明
}

function chomp( str ) { // 安直 chomp
return str.replace(/[\n\r]+$/, "" ); // もっと真面目にやれ。
}

function perlqx( str, opt ) { // perl の qx(`STRING`)相当
var fso = fso || new ActiveXObject("Scripting.FileSystemObject");
var WshShell = WshShell || new ActiveXObject("WScript.Shell");

// 標準出力
var sto = genTempPathEasy("sto");
var control = " 1> " + sto;
// 標準エラー出力
var ste = genTempPathEasy("ste");
control = control + " 2> " + ste;
var stat = WshShell.Run( "CMD /C " + str + control, 0, true );
var stdOut = file2text( sto );
fso.DeleteFile( sto, true );
if( opt ) var stdErr = file2text( ste );
fso.DeleteFile( ste, true );
return opt ? { "Status": stat, "StdOut": stdOut, "StdErr": stdErr } : stdOut ;

// %TEMP%フォルダーの一時ファイル名を安直に払い出す。
function genTempPathEasy( preFix, postFix ) {
// var fso = fso || new ActiveXObject("Scripting.FileSystemObject");
var tempPath = ( ((new Date()).toLocaleString()).replace( /([^\d])(\d)(?=[月日:]|$)/g, "$10$2" ) ).replace( /[^\d]+/g, "" );
if( preFix ) tempPath = preFix + tempPath;
if( postFix ) tempPath = tempPath + postFix;
tempPath = fso.BuildPath( fso.GetSpecialFolder(2), tempPath )
if( fso.FileExists( tempPath ) || fso.FolderExists( tempPath ) ) throw new Error("一時ファイルの作成に失敗しました。");
// 安直とはいえ存在チェックくらいはしておこう。
return tempPath;
}
// 指定ファイルを全読みしてテキストで返す。
function file2text( file ) {
if( ! fso.FileExists( file ) ) return null;
var f = fso.OpenTextFile( file, 1 ); // iomode 1:ForReading
var r = "";
if( ! f.AtEndOfStream ) r = f.ReadAll();
f.close();
return r;
}
/* 使ってねえや。
// 指定テキストをファイルに保存する。
function text2file( text, file ) {
var f = fso.CreateTextFile( file, true );
f.Write(text);
f.close();
return;
}
*/

}


いろいろ話したいネタのあるスクリプトだけど、とりあえず今日は記録を残しておくところまで。