[Tablacus Explorer] 現在or選択したディレクトリ以下のファイル・フォルダのパスをクリップボードにコピー

Tablacusで現在または選択したディレクトリ以下のファイルツリーをJSON形式で取得して クリップボードにコピーするTablacus用のJScript

使い方

JScriptを実行するとプロンプトのウィンドウが出てくるので出力オプションを指定する。

  • /: パス区切りを “/“ に変換
  • f: ファイルを列挙
  • d: ディレクトリを列挙
  • r: 再帰的な構造を生成
  • {数値}: 探索するフォルダ階層(規定は0)
  • 最初の”:”以降の文字列: ファイル名フィルタ(正規表現に一致するファイルのみ取得)
  • 2番目の”:”以降の文字列: フォルダ名フィルタ(正規表現に一致するフォルダのみ取得)’

オプション指定と結果の例

ディレクトリ構成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
dir0
0.htm
0.txt

└─dir1
1.htm
1.txt

└─dir2
2.htm
2.txt

└─dir3
3.htm
3.txt

オプション: /f1:\.html?$ 結果:

1
2
3
4
[
"C:/dir0/0.htm",
"C:/dir0/dir1/1.htm"
]

オプション: d99::dir[12]
結果:

1
2
3
4
5
[
"C:\\dir0",
"C:\\dir0\\dir1",
"C:\\dir0\\dir1\\dir2"
]

オプション: d99::dir[23] 結果: dir1が弾かれた結果、ルートディレクトリだけ出力される

1
2
3
[
"C:\\dir0"
]

オプション: /fdr1:\.txt 結果: false付いてるのは{数値}のオプションの深度上限に達したので探索中止した。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"directories": {
"C:/dir0": {
"files": [
"0.txt"
],

"directories": {
"dir1": {
"files": [
"1.txt"
],

"directories": {
"dir2": false
}

}

}

}

}

}

JScriptのソース

動作には問題ないと思うけど、再利用する気の起きないクソコードになってしまった…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
/**
* 指定したディレクトリ以下のオブジェクト名を取得する関数
* @param {string} targetDir 探索するルートディレクトリ
* @param {integer} depth 探索するサブディレクトリの深さ(正の整数)
* 0 を指定した場合は targetDir のみ
* 1 を指定した場合は targetDir のサブディレクトリの中まで
* n を指定した場合は察して下さい。
* @param {function} fileFilter = (fileName) -> bool
* ファイル名をこれに食わせて false が返ってきたらそのファイルは無視される
* @param {function} directoryFilter = (directoryName) -> bool
* ディレクトリ名をこれに食わせて false が返ってきたらそのファイルは無視される
* @param {object} _dest 再帰呼出で使うため指定しないこと
* @param {_currentDepth} 再帰呼出で使うため指定しないこと
* @return {object} 下のような構造のオブジェクト
* {
* files: [ファイルパスの配列],
* directories: {
* "ディレクトリ名1": 同様の構造のオブジェクト,
* "ディレクトリ名n": 同様の構造のオブジェクト,
* }
* }
*/

var GetFolderTree = function(
targetDir,
depth,
fileFilter,
directoryFilter,
_dest,
_currentDepth)
{

targetDir = targetDir.replace(/\\+$/, '');
if ( ! fso.FolderExists(targetDir)) return null;

fileFilter = fileFilter || function() { return true; }
directoryFilter = directoryFilter || function() { return true; }

var node = {};
_currentDepth = _currentDepth !== void 0 ? _currentDepth : 0;
if (depth !== -1 && depth < _currentDepth) {
node = false;
}

var dirName;
if (_dest) {
_dest.directories || (_dest.directories = {});
dirName = fso.GetFileName(targetDir);
} else {
_dest = { directories: {} };
dirName = targetDir;
}
_dest.directories[dirName] = node;

if (node === false) return _dest;

targetDir = targetDir.replace(/\\$/, '');
var folder = fso.GetFolder(targetDir);

var e = new Enumerator(folder.Files);
for ( ; !e.atEnd(); e.moveNext()) {
var file = e.item().Name;
if (fileFilter(file)) {
node.files || (node.files = []);
node.files.push(file);
}
}

e = new Enumerator(folder.SubFolders);
for ( ; !e.atEnd(); e.moveNext()) {
var dir = e.item().Name;
if (directoryFilter(dir)) {
GetFolderTree(
targetDir + '\\' + dir,
depth,
fileFilter,
directoryFilter,
node,
_currentDepth + 1);
}
}

var propExists = false;
for (var x in node) {
propExists = true;
break;
}
if ( ! propExists) _dest.directories[dirName] = null;

return _dest;
};

/**
* GetFolderTree で取得したオブジェクトを探索
* @param {object} tree GetFolderTreeの戻り値
* @param {function} onFindFiles = (fileArray, node, currentDirFullPath)
* ファイル配列のコールバック。currentDirFillPathはnullのことあり(ルートノードの場合)
* @param {function} onFindDirectoryName = (directoryName, node, currentDirFullPath)
* ディレクトリ名のコールバック。node[directoryName] で子ノード取得できる(nullのことあり)
* @param _currentDirectory 再帰呼出で利用するので指定しないこと
*/

var WalkTree = function(tree, onFindFiles, onFindDirectoryName, _currentDirectory) {
if (!tree) return;
onFindFiles = onFindFiles || function(){};
onFindDirectoryName = onFindDirectoryName || function(){};

if (tree.files && tree.files.length) {
onFindFiles(tree.files, tree, _currentDirectory);
}

if (tree.directories) {
for (var dirName in tree.directories) {
var fullDir = (_currentDirectory ? _currentDirectory + '\\' : '') + dirName;
onFindDirectoryName(dirName, tree, fullDir);

var node = tree.directories && tree.directories[dirName];
if (node) {
WalkTree(node, onFindFiles, onFindDirectoryName, fullDir);
}
}
}
};

/**
* GetFolderTree で取得したオブジェクトから
* 空のdirectoriesを取り除く
* @param {object} tree GetFolderTreeの戻り値
*/

var RemoveEmptyDirectoryFromTree = function(tree) {
WalkTree(tree, null, function onDirName(dirName, node) {
if (node.directories) {
var dirs = node.directories;
if ( ! dirs[dirName]) {
delete dirs[dirName];
}

var isEmpty = true;
for (var x in dirs) {
isEmpty = false;
break;
}
if (isEmpty) delete node.directories;
}
});
};

/**
* コマンドライン引数を配列にsplit
* @param {String} text コマンドライン文字列
* @param {Char} _delimChar 区切り文字1文字(
* @retrun {Array} 配列。nullは返さない。
*/

var SplitCommandLineArguments = function(text, _delimChar) {
var ret = [], delim = (_delimChar || ' ').charCodeAt(0), i = 0, c, quoteContext = false, fromPos = -1;
while(c = text[i]) {
c = c.charCodeAt(0);
if (quoteContext) {
if (c === 34) {
ret.push(text.substring(fromPos, i));
fromPos = -1;
quoteContext = false;
}
} else {
if (fromPos === -1) {
if (c === 34) {
quoteContext = true;
fromPos = i + 1;
} else if (c !== delim) {
fromPos = i;
}
} else {
if (c === delim) {
ret.push(text.substring(fromPos, i));
fromPos = -1;
}
}
}
i++;
}

if (fromPos !== -1) {
text = text.substr(fromPos);
for(i = text.length - 1; i >= 0; i--) {
if (text[i].charCodeAt(0) !== delim) {
ret.push(text.substr(0, i + 1));
break;
}
}
}

return ret;
};

//
// tablacus用の処理ここから
//
var description = [
'[ファイル一覧をクリップボードにコピー]',
'オプション指定方法(例: "/fr2:\.html?$"):',
'/: パス区切りを"/"に変換',
'f: ファイルを列挙',
'd: ディレクトリを列挙',
'r: 再帰的な構造を生成',
'{数値}: 探索するフォルダ階層(規定は0)',
'最初の":"以降の文字列: ファイル名フィルタ(正規表現に一致するファイルのみ取得)',
'2番目の":"以降の文字列: フォルダ名フィルタ(正規表現に一致するフォルダのみ取得)'
];

var options = (prompt(description.join(' '), '/f') || '').split(':');
var filterOptions = options.slice(1);
var options = options[0];

var i;

/* fileFilter, dirFilter の生成 */
var fileFilter;
var dirFilter;
!function(){
var fileRegex = filterOptions[0] && new RegExp(filterOptions[0], 'i');
if (options.indexOf('f') === -1) {
fileFilter = function(){ return false; };
} else if (fileRegex) {
fileFilter = function(fileName) { return fileRegex.test(fileName); };
} else {
fileFilter = function(fileName) { return true; };
}

var dirRegex = filterOptions[1] && new RegExp(filterOptions[1], 'i');
if (dirRegex) {
dirFilter = function(dirName) { return dirRegex.test(dirName); };
} else {
dirFilter = function(dirName) { return true; };
}
}();

/* ディレクトリ探索 */
var tree = (function() {
var depth = options.match(/\d+/);
depth = depth ? depth[0] * 1 : 0;

var currentDir = eventTE.Environment.current();
var selectedItems = SplitCommandLineArguments(eventTE.Environment.selected());
if (selectedItems[0]) {
var tree = { directories: {} };
var node = {};
tree.directories[currentDir] = node;

var path;
for (i = 0; path = selectedItems[i]; i++) {
path = path.replace(/^"/, '').replace(/"$/, '');
var name = fso.GetFileName(path);
if (fso.FileExists(path)) {
if (fileFilter(name)) {
node.files || (node.files = []);
node.files.push(name);
}
} else {
if (dirFilter(name)) {
GetFolderTree(path, depth, fileFilter, dirFilter, node, 1);
}
}
}
return tree;
} else {
return GetFolderTree(
currentDir,
depth,
fileFilter,
dirFilter);
}
})();

/* dオプションなしの場合は tree から空のディレクトリのプロパティを除去 */
var needsDir = options.indexOf('d') !== -1;
if ( ! needsDir) {
RemoveEmptyDirectoryFromTree(tree);
}

var ret;
if (options.indexOf('r') !== -1) {
/* r オプションありはそのまま */
ret = tree;
} else {
/* r オプションがない場合は単純な配列に変換 */
ret = [];
WalkTree(tree, function(files, node, dirFullPath) {
if (dirFullPath) {
for (i = 0; files[i]; i++) {
ret.push(dirFullPath + '\\' + files[i]);
}
}
}, function(dirName, node, dirFullPath) {
if (needsDir) {
ret.push(dirFullPath);
}
});
}

/* クリップボードに貼り付けて終了 */
var data = JSON.stringify(ret, null, '\t');
if (options.indexOf('/') !== -1) {
/* ファイル名には "\" が使えないので雑な変換で問題なし */
data = data.replace(/\\\\/g, '/');
}
clipboardData.setData("text", data);