JScript on WSHでディレクトリ(フォルダ)を再帰的に辿る処理の雛形

2009/04〜2009/05ぐらいだったか、あるディレクトリ以下の階層に潜っていって最下層のディレクトリが特定の名前だったらその中にディレクトリを作る、という作業をやろうとして、開始5秒で手作業でやることに決定したことがあった。

悔しいので今更ながらリベンジすることにした。

いきなりファイルを弄るのは危ないので、指定したディレクトリをルートとして、ルート以下の階層のディレクトリとファイルを階層的に表示させてみた。ディレクトリを指定しなかった場合は、カレントディレクトリをルートとする仕様。このスクリプトを雛形に、必要に応じて書き換えることで対応するつもり。

/// enum_dir.js
/// ディレクトリを再帰的に辿り、ファイル/ディレクトリを階層的に表示する
/// 
/// JScript on WSH 5.6 @ Windows XP SP2
/// cscriptで実行しないと、ファイル等が1つずつ表示されるので大変な目に遭う。

(function() {

	/// このスクリプトを実行しているエンジンの名前を取得する。
	/// cscript, wscript, WScript の何れかが返ってくるはず。
	var engine_name = function() {
		var fsys = WScript.CreateObject("Scripting.FileSystemObject");
		var name = fsys.GetBaseName(WScript.FullName);
		return function() { return name; };
	}();

	// wscript で実行する場合、使用可能なメッセージ表示関数は Echo のみ。
	// cscript なら標準出力と標準エラー出力を分けて使用できる。

	/// メッセージを表示する
	var println = function(s) { WScript.Echo(s); };

	/// エラーメッセージを表示する
	var eprintln = function() {
		// コンソールで実行中ならtrueを返す
		var isconsole = function() {
			return (engine_name() === "cscript") ? true : false;
		};

		if (isconsole()) {
			return function(s) { WScript.StdErr.WriteLine(s); };
		} else {
			return function(s) { WScript.Echo(s); };
		}
	}();

	/// カレントディレクトリ名を返す
	var pwd = function() {
		var sh = WScript.CreateObject("WScript.Shell");
		var dir = sh.CurrentDirectory;
		return function() { return dir; };
	}();

	/// 指定されたフォルダ以下の階層のフォルダを列挙する。
	/// fn_pre  には、下位フォルダを列挙する前に実行したい関数をセットする。
	/// fn_post には、下位フォルダを列挙した後に実行したい関数をセットする。
	var enum_dir = function(folder, fn_pre, fn_post, lv) {
		var subfolders;

		if (lv === undefined) {
			lv = 0;
		}
		fn_pre(folder, lv);

		subfolders = new Enumerator(folder.SubFolders);
		for (; !subfolders.atEnd(); subfolders.moveNext()) {
			enum_dir(subfolders.item(), fn_pre, fn_post, lv+1);
		}

		fn_post(folder, lv);
	};

	/// 空白文字を生成する
	var make_space = function(nsp) {
		var sp, i;

		if (nsp === 0) {
			return "";
		}

		sp = [];
		for (i = 0; i < nsp; ++i) {
			sp.push("  ");
		}
		return sp.join("");
	};

	/// フォルダ名を表示する
	var print_dirname = function(folder, lv) {
		println(make_space(lv) + folder.Name + "/");
	};

	/// ファイル名を逐次表示する
	var print_files = function(folder, lv) {
		var files = new Enumerator(folder.Files);

		for (; !files.atEnd(); files.moveNext()) {
			println(make_space(lv+1) + files.item().Name);
		}
	};

	/// メインルーチン
	(function (progname, args) {
		var fsys = WScript.CreateObject("Scripting.FileSystemObject");
		var root_dir = function(dir_name) {
			return fsys.GetFolder(dir_name);
		};
		var i, nprinted;

		if (args.length <= 0) {
			enum_dir(root_dir(pwd()), print_dirname, print_files);
		} else {
			for (i = nprinted = 0; i < args.length; ++i) {
				if (fsys.FolderExists(args(i))) {
					if (nprinted > 0) {
						println("");
					}
					enum_dir(root_dir(args(i)), print_dirname, print_files);
					++nprinted;
				} else {
					eprintln(progname + ": " + args(i) + ": No such file or directory")
				}
			}
		}
	})(WScript.ScriptName, WScript.Arguments);

})();

面倒なので再帰で書いたので、あまり階層が深すぎると動かないかもしれない。