1

Тема: CMD/BAT + VBScript: два в одном

По аналогии с темами
CMD/BAT + JavaScript: два в одном
JScript:CMD - вариант с условной компиляцией

echo off

::sub echo(a):end sub
::sub vbscript(a):end sub
::nologo=1:e=1:e=_
cscript /nologo /e:vbscript "%~f0"

set e=nothing'&&set e=&&exit /b %errorlevel%

'Option Explicit

WScript.Echo "Hello, world!"

Можно задавать код возврата из программы на VBScript. Но!

Решение имеет 2 существенных недостатка почти одинаковой значимости:
1. невозможно передать аргументы командной строки
2. невозможно избавиться от вывода первой строки командного интерпретатора

Существует еще один незначительный недостаток, которым в целом можно пренебречь:
3. невозможно использовать Option Explicit

Посему, решение имеет очень низкую ценность.

( 2 * b ) || ! ( 2 * b )

2

Re: CMD/BAT + VBScript: два в одном

Есть еще один способ запустить VBScript из батника - через JScript.

@set @script=0 /*
   @echo off
     set @script=
     cscript //nologo //e:jscript "%~dpnx0"
   exit /b
*/

code = '';
code += 'intHNum = 100' + "\n";
code += 'intLNum = 1' + "\n";
code += 'For i = 1 To 5' + "\n";
code += 'Randomize' + "\n";
code += 'intNum = Int((intHNum - intLNum + 1) * Rnd + intLNum)' + "\n";
code += 'WScript.Echo intNum' + "\n";
code += 'Next';

var vb = new ActiveXObject("MSScriptControl.ScriptControl");
vb.language = "VBScript";
vb.addobject('WScript', WScript, true);
vb.addcode(code);

Это, конечно, всего лишь пример.

3

Re: CMD/BAT + VBScript: два в одном

greg zakharov
Нет. Это не то. Так можно и ассемблерный и машинный код запустить. Весь vbs-код сокрыт в строковых константах, что не есть хорошо, потому что
- нет подсветки кода,
- проблема с выводом сообщений об ошибках,
- трудности в отладке.

( 2 * b ) || ! ( 2 * b )

4 (изменено: greg zakharov, 2011-12-10 16:14:56)

Re: CMD/BAT + VBScript: два в одном

Rumata пишет:

Так можно и ассемблерный и машинный код запустить.

Ни примера кода от вас, ни достойных аргументов я не увидел, а потому пример все же имеет право на жизнь.

Rumata пишет:

Весь vbs-код сокрыт в строковых константах...

Точнее константе.

Rumata пишет:

- нет подсветки кода

А для чего? Кому? И вообще зачем?

Rumata пишет:

- проблема с выводом сообщений об ошибках

Отнюдь, уважаемый. Прежде, чем запихивать VBScript в JS, должны же провестись клинические испытания на работоспособность пациента, в противном случае писать просто наобум, знаете, как-то однако.

Rumata пишет:

- трудности в отладке

Это всего лишь интерпретация ваших же слов "- проблема с выводом сообщений об ошибках", - так что все три пункта лично мне не показались весомыми.
Жду примеров касательно первого пункта.

5 (изменено: Rumata, 2011-12-10 16:31:58)

Re: CMD/BAT + VBScript: два в одном

greg zakharov пишет:

Ни примера кода от вас, ни достойных аргументов я не увидел

Не придавайте серьезности этим словам.

greg zakharov пишет:

Прежде, чем запихивать VBScript в JS, должны же провестись клинические испытания на работоспособность пациента

Естественно есть тесты и все прочее. Но даже протестированные программы когда-нибудь да дают сбой.

Представьте, что есть некоторая программа, которая осуществляет некоторые действия Были проведены всевозможные широкомасштабные проверки на вшивость - программа работает. Но ведь невозможно всего предусмотреть... И вот приходит баг-репорт:

(14, 1) Microsoft VBScript runtime error: Division by zero

В какой строке этого проекта произошел сбой?

@set @script=0/*
@cscript //nologo //e:jscript "%~dpnx0"
@goto :EOF */

code = [
    'While True', 
        'X = 1 / Rnd', 
        'Y = 1 / Rnd', 
    'Wend', 
].join('\n');

var vb = new ActiveXObject('MSScriptControl.ScriptControl');
vb.language = 'VBScript';
vb.addcode(code);
( 2 * b ) || ! ( 2 * b )

6

Re: CMD/BAT + VBScript: два в одном

Rumata, все просто

@set @script=0/*
@cscript //nologo //e:jscript "%~dpnx0"
@goto :EOF */

code = [
    'While True', 
        'X = 1 / Rnd', 
        'Y = 1 / Rnd', 
    'Wend', 
].join('\n');

var vb = new ActiveXObject('MSScriptControl.ScriptControl');
vb.language = 'VBScript';
try
{
vb.addcode(code);
}
catch (e)
{
 WScript.Echo(vb.Error.Line);
}

7

Re: CMD/BAT + VBScript: два в одном

JSman пишет:

vb.Error.Line

Согласен.

( 2 * b ) || ! ( 2 * b )

8

Re: CMD/BAT + VBScript: два в одном

Уважаемый Rumata, господин JSman меня опередил с ответом, за что я бы хотел ему выразить свою благодарность, а вас спросить: так вы не исключаете возможность использования VBScript через JS?

9

Re: CMD/BAT + VBScript: два в одном

greg zakharov, удобнее код хранить при помощи ресурсов

Хотел бы еще дополнить, что стоит протестировать ScriptControl с внедренным WScript на полноценность работы  метода WScript.Create(ProgID, Prefix).

10

Re: CMD/BAT + VBScript: два в одном

greg zakharov пишет:

не исключаете

Нет ). Но самое главное заключается в том, что vbs-код заключен в строковые литералы и нельзя посмотреть vbs-код, так как он - набор строк. В отличие от этого комбинации bat+js или bat+perl (по-видимому идея комбинации пошла оттуда) позволяют посмотреть любой код в редакторе.

И тут мне подумалось

JSman пишет:

vb.Error.Line

это и хорошо - так как показывать номер строки где произошла ошибка, и плохо - так как она показывает номер относительно vbs-кода, а не всего файла.

( 2 * b ) || ! ( 2 * b )

11

Re: CMD/BAT + VBScript: два в одном

Если базовая часть везде одинакова, то можно  просто прибавлять количество строк.

12

Re: CMD/BAT + VBScript: два в одном

Передавать аргументы все-таки можно:


echo off
:sub echo(off):exit sub
set off=""'&set off=&cscript /nologo /e:vbscript "%~f0" %*&exit /b %errorlevel%
:end sub

WScript.Echo "Hello, world!"

Но от пресловутой первой строки

echo of

не избавиться. Кстати MSScriptControl.ScriptControl планомерно выводится со сцены: в win7 уже такого объекта нет.

( 2 * b ) || ! ( 2 * b )

13

Re: CMD/BAT + VBScript: два в одном

Кстати MSScriptControl.ScriptControl планомерно выводится со сцены: в win7 уже такого объекта нет.

Так его и раньше не было.

14 (изменено: Flasher, 2014-01-19 07:44:12)

Re: CMD/BAT + VBScript: два в одном

Как это нет (не было) в win7? Я им (правда без приставки MSScriptControl.) с 7-ки и начал пользоваться. Хотя он был ещё со времён win2000.

15

Re: CMD/BAT + VBScript: два в одном

В комплекте поставки не было. Отдельно ставить приходилось.

16

Re: CMD/BAT + VBScript: два в одном

И сейчас можно его поставить, хоть на 7, хоть на 8. Проблема только в том, что у него нет 64-битной версии, а скрипты по умолчанию запускаются 64-битным скриптхостом. Но никто не мешает изменить умолчание.

17

Re: CMD/BAT + VBScript: два в одном

alexii пишет:

В комплекте поставки не было. Отдельно ставить приходилось.

Очень странно. Никогда не сталкивался (что на первых версиях Win7, что на SP1, что на XP).

18

Re: CMD/BAT + VBScript: два в одном

Я в начале скриптов проверяю систему систему на битность. Всегда использую только 32-битный wscript.

19 (изменено: Serge Yolkin, 2014-01-19 21:26:58)

Re: CMD/BAT + VBScript: два в одном

alexii пишет:

В комплекте поставки не было. Отдельно ставить приходилось.

Господи! Что ж я такого понаставил, что и на 7(SP1) и на 8.1 работает? (x86)

20

Re: CMD/BAT + VBScript: два в одном

Не знаю. У меня нет ни того, ни другого.

21 (изменено: wisgest, 2014-01-19 23:23:41)

Re: CMD/BAT + VBScript: два в одном

Rumata пишет:

2. невозможно избавиться от вывода первой строки командного интерпретатора

Существует еще один незначительный недостаток, которым в целом можно пренебречь:
3. невозможно использовать Option Explicit

Коль нельзя исправить 2-е, можно справиться с 3-м.
Option Explicit должен быть первым оператором — даже пустые операторы перед ним не допускаются, но возможны комментарии:

': 2>nul&CScript.exe /nologo /e:vbs "%~f0" %*&exit /b

Option Explicit
WScript.Echo "Hello, world!"

Первой командой производится попытка запуска заведомо несуществующего файла (если бы был просто апостроф, то файл с таким именем мог бы существовать) с подавлением сообщения об ошибке.
Для сокращения паразитного вывода в начало можно добавить:

': 2>nul&echo off

хотя мне кажется предпочтительнее видеть строку запуска бессодержательному «echo off».

EXIT /B не требует после себя %errorlevel%, т.к. без аргумента он и так его возвращает (а не 0, как можно было предположить).

22

Re: CMD/BAT + VBScript: два в одном

EXIT /B не требует после себя %errorlevel%, т.к. без аргумента он и так его возвращает (а не 0, как можно было предположить).

С тем же результатом можно использовать GOTO :EOF.

( 2 * b ) || ! ( 2 * b )

23

Re: CMD/BAT + VBScript: два в одном

Rumata пишет:

невозможно избавиться от вывода первой строки командного интерпретатора

Т.к. первое решение в теме «CMD/BAT + JavaScript: два в одном» обладает тем же недостатком, то если не будет возражений и не будет найдено способов устранить этот недостаток, то спустя какое-то время добавлю решение #21 в коллекцию.

24

Re: CMD/BAT + VBScript: два в одном

В той теме приведены другие решения (2, 5, 6), которые лишены настоящих недостатков.

Относительно текущей темы: из трех зол выбираем меньшее и помещаем в коллекцию.

( 2 * b ) || ! ( 2 * b )

25 (изменено: wisgest, 2014-01-22 00:35:15)

Re: CMD/BAT + VBScript: два в одном

Rumata пишет:

В той теме приведены другие решения (2, 5, 6), которые лишены настоящих недостатков.

Вы о теме JScript in batch? Если так, то я это знаю и что-то похожее использую.
Речь была о минимально допустимом уровне.

Rumata пишет:

Относительно текущей темы: из трех зол выбираем меньшее и помещаем в коллекцию.

Относительно этой темы, то я только обозначил намерение и до конца этого месяца ничего предпринимать не собирался. А какое из зол меньшее?
Мне свой вариант кажется менее отягощённым вспомогательными построениями.

26

Re: CMD/BAT + VBScript: два в одном

Главное — помещайте .

27 (изменено: Dragokas, 2014-02-01 23:01:01)

Re: CMD/BAT + VBScript: два в одном

Просто мысли по доработке идеи от greg zakharov-а (BAT -> JScript -> VBScript).

+ обработка ошибок
+ VBScript в исходном виде, удобном для отладки

- нет передачи аргументов
- громоздкость кода
- запуск от имени 32-разрядного сервера сценариев

@set @x=0 /*
  @echo off & set "x="
  Set "xOS=x64"& If "%PROCESSOR_ARCHITECTURE%"=="x86" If Not Defined PROCESSOR_ARCHITEW6432 Set "xOS=x32"
  if "%xOS%"=="x64" (set "engine=%windir%\syswow64\cscript.exe") else (set "engine=cscript.exe")
  "%engine%" //nologo //e:jscript "%~f0" & goto :eof

' Ваш код VBScript
Randomize
For i = 1 to 5
  n = n & vbLf & int(Rnd*50)
Next
WScript.Echo (n)
' Сгенерируем ошибку
n = 1 \ 0

*/
var ofso = new ActiveXObject("Scripting.FileSystemObject");
var oTS = ofso.OpenTextFile(WScript.ScriptFullName, 1, false);
for (i = 0; i < 5; i++) {oTS.SkipLine()}; var code = '', s = '';
do
{
  code += s + "\n";
}
while ((s = oTS.ReadLine()).slice(0,2) != '*/');

var vb  = new ActiveXObject("MSScriptControl.ScriptControl");
vb.language = "VBScript";
vb.addobject('WScript', WScript, true);
try { vb.addcode(code); } catch (e) {}
var e = vb.Error; 
if (e.Number != 0) WScript.Echo('('+(e.Line+4) + ','+e.Column + ') Error '+e.Number+': '+e.Description);

Объект "MSScriptControl.ScriptControl" можно создать только из 32-разрядного процесса.

28 (изменено: Rumata, 2014-02-02 07:00:56)

Re: CMD/BAT + VBScript: два в одном

Ага. А я-то думал, что в 64-битных системах ScriptControl уже не доступен. Надо просто использовать 32-битную версию скрипт-хоста.

На самом деле коллега greg zakharov показал в какую сторону смотреть. Если уж допиливать эту реализацию, то лучше уйти от точных значений количества строк пролога. А в случае ошибок (синтаксических или времени выполнения) точно указывать на строку в файле. Можно сделать так:


@if (true == false) @end /*
@echo off

set "SYSDIR=SysWOW64"
if "%PROCESSOR_ARCHITECTURE%" == "x86" if not defined PROCESSOR_ARCHITEW6432 set "SYSDIR=System32"

"%WINDIR%\%SYSDIR%\cscript.exe" //nologo //e:javascript "%~f0" %*

goto :EOF

*/

(function(readFile, code)
{
    var e;
    try {
        var vb = new ActiveXObject('MSScriptControl.ScriptControl');
        vb.Language = 'VBScript';
        vb.AddObject('WScript', WScript, true);
        vb.AddCode(code);
    } catch(e) {
        var file = readFile();
        var prologLen = file.slice(0, file.indexOf(code)).split('\n').length;
        var vbe = vb.Error;
        WScript.Echo(
            WScript.ScriptFullName + 
            '(' + ( prologLen + vbe.Line - 1 ) + ', ' + vbe.Column + ') ' + 
            vbe.Source + ': ' + vbe.Description);
    }
})(
function()
{
    var fso = new ActiveXObject('Scripting.FileSystemObject');
    var f = fso.OpenTextFile(WScript.ScriptFullName, 1, true);
    var file = f.ReadAll();
    f.Close();
    return file;
}, 
(function()
{
    return arguments.callee.toString().replace(/^[\s\S]+\/\*|\*\/[\s\S]+$/g, '');
/*

WScript.Echo "Hello, world!"
WScript.Quit 1000

*/
})());

Небольшое пояснение.
Основная функция исполняет vbs-код, переданный в code. В случае ошибки (синтаксической или времени выполнения) считывается весь файл с помощью readFile и вычисляется количество строк, предшествующих vbs-коду, для правильной идентификации строки, вызвавшей ошибку.

Положительных моментов два:
1. размер "пролога" не задан жестко - он вычисляется на ходу
2. для вычисления размера пролога считывается исходный файл только в случае возникновения ошибки

Другие положительные моменты:
1. vbs-код встроен таки в bat-файл
2. Допустим любой правильный vbs-код
3. код работает, понимает аргументы командной строки, читает/пишет из/в стандартные потоки и возвращает код возврата

По пункту 2 есть одно исключение. vbs-код НЕ ДОЛЖЕН содержать последовательность символов */ в любом виде (в строках или комментариях). Если требуется такая комбинация, то рекомендуется использовать следующее решение:

S = "*" & "/"
( 2 * b ) || ! ( 2 * b )

29

Re: CMD/BAT + VBScript: два в одном

Rumata пишет:

2. для вычисления размера пролога считывается исходный файл только в случае возникновения ошибки

А при возникновении ошибки исходный файл может уже не существовать или быть совсем другим (хотя, конечно, зачем в этом случае знать место ошибки).

+ Попроще
@if (true == false) @end /*
@echo off

set "SYSDIR=SysWOW64"
if "%PROCESSOR_ARCHITECTURE%" == "x86" if not defined PROCESSOR_ARCHITEW6432 set "SYSDIR=System32"

"%WINDIR%\%SYSDIR%\cscript.exe" //nologo //e:javascript "%~f0" %*

goto :EOF

*/

var prologLen = 0;
var vb = new ActiveXObject('MSScriptControl.ScriptControl');
vb.Language = 'VBScript';
vb.AddObject('WScript', WScript, true);
try {
    vb.AddCode(
(function () {
var fso = new ActiveXObject("Scripting.FileSystemObject");
var f = fso.OpenTextFile(WScript.ScriptFullName, 1, false);
do prologLen++; while (f.ReadLine() != "/* VBScript");
var code = f.ReadAll();
f.Close();
return code;
})()
    );
} catch (e) {
    var vbe = vb.Error;
    WScript.StdErr.WriteLine(
        WScript.ScriptFullName + 
        '(' + ( prologLen + vbe.Line ) + ', ' + vbe.Column + ') ' + 
        vbe.Source + ': ' + vbe.Description);
}
/* VBScript

WScript.Echo "Hello, world!"
WScript.Quit 1000

'*/

Пролог считывается построчно, vbs-код — целиком.
В памяти не остаётся переменных JScript кроме vb и prologLen, — code возаращается анонимной функцией.

30 (изменено: Rumata, 2014-02-03 11:54:23)

Re: CMD/BAT + VBScript: два в одном

Попроще варианты я тоже рассматривал - с разным расположением vbs-кода относительно js-кода. Я остановился на этом варианте именно чтобы исключить любые вспомогательные глобальные js-переменные. Хотя это проблем не вызывает - vbs не имеет к ним доступа.

В памяти не остаётся переменных JScript кроме vb и prologLen

Там есть еще одна переменная - e.

Мне кажется, идея использовать фиксированные строки-маркеры (например /* VBScript, как в Вашем случае) хорошая, но не вполне удобная.
js-функция, которая возвращает содержимое своего комментария мне видится лучше - маркеры уже заданы (js-комментарии) и автоматически обработаны самим js-интерпретатором. Мы с JSman обсуждали реализацию такой функции и ее эффективность - JS: Хранение ресурсов в файле JS. К сожалению ссылка уже не существует.

при возникновении ошибки исходный файл может уже не существовать или быть совсем другим

Мысль здравая. Для указания на источник ошибки (номер строки в исходном, неизмененном файле) необхоимо заранее считывать весь файл.

( 2 * b ) || ! ( 2 * b )

31

Re: CMD/BAT + VBScript: два в одном

Rumata пишет:

Я остановился на этом варианте именно чтобы исключить любые вспомогательные глобальные js-переменные. Хотя это проблем не вызывает - vbs не имеет к ним доступа.

Конечно, это проблем вызвать не может, поэтому не существенно. Просто ко времени исполнения vbs хотелось бы не оставлять в памяти js-переменные, которые далее не будут востребованы (глобальные или локальные — не имеет значения) или без которых можно попробовать обойтись.

32 (изменено: Rumata, 2014-04-21 14:43:01)

Re: CMD/BAT + VBScript: два в одном

Буржуи поведали шикарный код http://www.dostips.com/forum/viewtopic. … 882#p33882.


::'@echo off
::'cscript //nologo //e:vbscript "%~f0" %*
::'exit /b

WScript.Echo "Hello, world!"

Фишка:
После каждого символа апострофа стоит невидимый символ SUB (ASCII 26, 1A)

Как это работает
cmd/bat
:: начинаются обычные cmd-комментарии, внутри которых присутствует символ SUB.
SUB интерпретируется как разрыв строки, что значит, что выражение следом будет выполнено.

vbs
: - разделитель команд (x = 0 : y = 0). Не имеет значения если они написаны в начале строки.
Апостороф ' начинает vbs-комментарий. Следующий символ SUB является частью комментария и не влияет ни на что.

( 2 * b ) || ! ( 2 * b )

33 (изменено: wisgest, 2015-06-03 15:33:07)

Re: CMD/BAT + VBScript: два в одном

Rumata пишет:

После каждого символа апострофа стоит невидимый символ SUB

Можно ограничиться только одним непечатным знаком в первой строке, переписав остальные в виде

rem:& команда

(без пробелов между «rem» и «:»).

Мне, впрочем, «Option Explicit» милее полного подавления отображения команд пакетного файла, поэтому вернусь к решению http://forum.script-coding.com/viewtopi … 346#p79346:

wisgest пишет:

Option Explicit должен быть первым оператором — даже пустые операторы перед ним не допускаются, но возможны комментарии:

': 2>nul&CScript.exe /nologo /e:vbs "%~f0" %*&exit /b

— сказанное относится и к комментариям, созданным с помощью ключевого слова REM, поэтому можно обойтись без попыток обращения к несуществующему файлу/диску и подавления сообщения об ошибке:

rem:& @echo off
rem:& setlocal enableextensions disabledelayedexpansion
rem:& CScript.exe -nologo -i -t:0 -e:vbs" %~f0" %*
rem:& endlocal& exit /b

Option Explicit
Dim str
str = "Hello, world!"
WScript.Echo str

— заметим, что часть первой строки после «@» не отображается и на выход попадает только (с точностью до %PROMPT%):

C:\Documents and Settings\user\Рабочий стол>rem: &

(можно после этого «rem:» вставить какую-нибудь приличествующую случаю поучительную или пояснительную надпись, например, «VBScript in "%~nx0"»).

Ну и, конечно, не следует забывать о команде CLS. Но она поможет только при запуске из Проводника или другого файлового приказчика, а как быть с перенаправлением вывода?
Не знаю, как у вас, а у меня перенаправление вывода в загадочный для меня поток с дескриптором &3, в отличии от дескрипторов с большими значениями, приводит без дополнительных действий к выводу в окно консоли также как для &1 (stdout) и &2 (stderr).
Поэтому, в отсутствие перенаправления строка

rem:& CScript.exe -nologo -i -t:0 -e:vbs" %~f0" %*>&3

работает по прежнему, а при перенаправлении лишнее легко убрать:

vbs.bat 1>nul 3>log.txt
(vbs.bat 1>nul) 3>&1| more