Тема: HTA/JScript: Автоматический разбор параметров командной строки
Начало разговора в теме JavaScript: форматирование числа. Предлагаю продолжить обсуждение здесь.
Задача ставится следующим образом. Необходимо разобрать командную строку, с помощью которой было запущено приложение HTA. Известно, что WSH приложения имею мощный интерфейс для работы с параметрами командной строки как со всеми аргументами, так и с отдельными типами (именованные или неименованные):
// все аргументы
var args = WScript.Arguments;
for (var i = 0; i < args.length; i++) {
var arg = args.item(i);
WScript.Echo(arg);
}
// неименованные, или безымянные аргументы
var uArgs = WScript.Arguments.Unnamed;
for (var i = 0; i < uArgs.length; i++) {
var arg = uArgs.item(i);
WScript.Echo(arg);
}
// именованные аргументы
var nArgs = WScript.Arguments.Named;
for (var e = new Enumerator(nArgs); ! e.atEnd(); e.moveNext()) {
var k = e.item(i);
var v = nArgs.item(k);
WScript.Echo(k + ' = ' + v);
}Как видно, именованные аргументы имеют несколько отличный способ доступа к самим значениям, но это не вызывает проблемы. Видно, что аргументы "унифицируются" - начальные и конечные кавычки удаляются (хотя удаляются, на мой взгляд, не совсем корректно и существует одна проблема с именованными аргументами, о которой можно поговорить отдельно).
Однако HTML-приложения таких удобных средств для работы с командной строкой не имеют. Существует только свойство commandLine строкового типа доступное из тега <hta:application>. То есть все аргументы в одной куче. Разработчику самому предоставляется возможность решать - как ему интерпретировать переданную информацию.
В ходе обсуждения в теме предложен способ, который позволяет решить данную проблему:
var re = /"(.*?)"(?:\s+|$)|([\S]+)/g;
var r;
while ( r = re.exec(s) ) {
var arg = r[1] || r[2];
alert(arg);
}Плюсами данного решения являются:
-- лаконичность
-- универсальность
Можно было бы на этом и остановиться, но некоторое время назад я решил пойти дальше и создал монстра - модуль более чем в 250 строк кода, комментариев и пустых строк, претендующий на универсальность. Полный разбор всех командной строки, группировка аргументов на именованные и строковые, автоматическое создание строковых, именованных, логических и простых аргументов.
Очевидно, что в погоне за универсальностью чаще всего создается монстр; вероятно, что данное решение содержит недочеты, которые не были обнаружены изначально, и могут всплыть позже.
Интерпретация аргументов:
строковые аргументы:
строка - простая строка. не содержащая пробелы
"строка" - простая строка, допускаются пробелы и кавычкипростые именованные аргументы:
/ключ - значение ключа - пустая срока, проверяется наличие ключалогические ключи:
/ключ- - значение ключа false
/ключ+ - значение ключа trueстроковые аргументы:
/ключ:значение - значение ключа - строка без пробелов и кавычек
/ключ:"значение" - значение ключа - любая строка символов, включая пробелы и кавычки
В результате работы создается глобальный объект HTA, содержащий несколько свойств и методов
appFullName
Возвращает полный путь текущего приложения.appPath
Возвращает только путь к приложению.appName
Возвращает только имя файла текущего приложения.cmdLine
Возвращает командную строку.arguments
Возвращает полный список всех аргументов, переданных приложению.Методы: item(index), length()
arguments.unnamed
Список строковых аргументов в виде массива.Методы: item(index), length()
arguments.named
Возвращает объект-список именованных аргументов.
Каждый ключ это массив значений, даже если передано было одно значение.
Таким образом можно передать любое количество одноименных ключей.Именованный аргумент может быть простым, логическим или строковым.
Логический аргумент передается приложению как /ключ+, чтобы
передать значение true или /ключ-, чтобы передать значение false.Простой именованный аргумент не имеет значений и передается как /ключ.
Строковый аргумент передается приложению как пара /ключ:значение.
Методы: exists(key), item(key), keys(), length()
.
Ниже приведен тестовый файл, который выводит все аргументы, переданные скрипту. Для проверки необходимо поэкспериментировать с разным набором и количеством аргументов.
test.hta
<html>
<head>
<script src="HTA.js"></script>
</head>
<body>
<pre>
<script>
document.writeln('Full name : ', HTA.appFullName);
document.writeln('Path name : ', HTA.appPath);
document.writeln('File name : ', HTA.appName);
document.writeln('Command Line : ', HTA.cmdLine);
function printArgs(name, args)
{
var keys = args.keys ? args.keys() : [];
var len = keys.length || args.length();
document.writeln();
document.writeln('<h3>', name + ' (<code>' + len + '</code>)</h3>');
for (var i = 0; i < len; i++) {
var k = keys[i] || i;
document.writeln(k, ' : ', args.item(k));
}
};
printArgs('Common', HTA.arguments);
printArgs('Unnamed', HTA.arguments.unnamed);
printArgs('Named', HTA.arguments.named);
</script>
</pre>
</body>
</html>.
HTA.js
(function()
{
// Duck-typing check for MSIE engine
if ( this !== this.window ) {
return;
}
var d = this.document;
if ( ! d || ! d.all || ! d.getElementsByTagName || ! d.createElement || ! d.insertBefore ) {
return;
}
// Create the hta:application tag if it is necessary
var htApp = d.getElementsByTagName('application')[0];
if ( ! htApp ) {
htApp = d.createElement('hta:application');
var head = d.getElementsByTagName('head')[0];
head.insertBefore(htApp, head.firstChild);
}
// Only HTAs have this property
var cmdLine = htApp.commandLine;
if ( ! cmdLine ) {
return;
}
var re = new RegExp([
// named arguments rounded or not by the quote characters
// /named
// where the 'named' term stands for one of the following:
// /key+ key is true
// /key- key is false
// /key
// /key:
// /key:"" key is an empty string
// /key:value
// /key:"value" key contains the string 'value'
'(',
'\\/',
'([\\S]+?)',
'(?:',
// /key+
'(\\+)',
'|',
// /key-
'(\\-)',
'|',
// /key:"value"
// /key:""
':"(.*?)"',
'|',
// /key:value
// /key:
':([\\S]*)',
')?',
'|',
// unnamed arguments rounded by the quote characters
// "value"
'"(.*?)"',
'|',
// unnamed arguments without the quote characters
// value
'([\\S]+)',
')',
'(?:\\s+|$)',
].join(''), 'g');
var r;
// It's impossible but we are paranoic
r = re.exec(cmdLine);
if ( ! r ) {
return;
}
var fullname = (r[7] || r[8]).replace(/"/g, '');
var filename = fullname.replace(/^.+\\/, '');
var pathname = fullname.replace(/\\[^\\]+$/, '');
var args = [];
var unnamed = [];
var named = {};
var named_length = 0;
var named_keys = [];
while ( r = re.exec(cmdLine) ) {
var k = r[2];
var v = r[3] ? true : r[4] ? false : r[5] || r[6] || r[7] || r[8];
if ( ! k && ! v ) {
continue;
}
args.push(r[1]);
if ( ! k ) {
unnamed.push(v);
continue;
}
named_length++;
named[k] = (named[k] || []).concat(v);
(function()
{
for (var i = 0; i < named_keys.length; i++) {
if ( k == named_keys[i] ) {
return;
}
}
named_keys.push(k);
})();
}
var HTA = {
arguments: {
named: {
exists: function(key)
{
return named.hasOwnProperty(key);
},
item: function(key)
{
return named[key];
},
keys: function()
{
return [].concat(named_keys);
},
length: function()
{
return named_keys.length;
}
},
unnamed: {
item: function(index)
{
return unnamed[index];
},
length: function()
{
return unnamed.length;
}
},
item: function(index)
{
return args[index];
},
length: function()
{
return args.length;
}
},
appFullName: fullname,
appPath: pathname,
appName: filename,
cmdLine: cmdLine
};
this.HTA = HTA;
})();
