Можно ли встраивать и выполнять VBScript в пакетном файле без использования временного файла?

Люди в течение длительного времени встраивали и выполняли VBScript в пакетных файлах. Но все опубликованные решения, которые я видел (в то время, когда этот вопрос был первоначально поставлен), включали в себя запись временного файла VBS. Например: Вставить VBScript внутри пакетного файла Windows.

Возможно ли выполнить встроенный VBScript внутри пакета без записи временного файла?

Ответ 1

Примечание. Перейдите в раздел UPDATE 2014-04-27 в нижней части этого ответа для лучшего решения.

Раньше я думал, что ответа нет. Но затем пользователь DosTips Liviu обнаружил, что символ <SUB> (Ctrl-Z, 0x1A, decimal 26) имеет эффекты bizare при встраивании в пакетный файл. Если функции несколько похожи на терминатор строк, так что командные команды, которые следуют за REM (или :: примечанием хаком), могут выполняться, если им предшествует Ctrl-Z. http://www.dostips.com/forum/viewtopic.php?p=13160#p13160

Это было подтверждено на XP Home Edition sp3, Vista Home Premium sp2 64 бит и Vista Enterprise sp2 32 бит. Я предполагаю, что он работает с другими версиями Windows.

Примечание. Приведенный ниже код должен иметь встроенные символы Ctrl-Z. Клянусь, я видел их на этом сайте при просмотре с помощью IE8. Но они, кажется, были потеряны из этого поста, и я не могу понять, как их публиковать. Я заменил символы строкой <SUB>

::<sub>echo This will execute in batch, but it will fail as vbs.
rem<SUB>echo This will execute in batch, and it is also a valid vbs comment
::'<SUB>echo This will execute in batch and it is also a valid vbs comment

Это ключ к успешному гибриду batch/vbs. Пока каждой команде пакета предшествует rem<SUB> или ::'<SUB>, тогда движок vbs его не увидит, но команда пакета будет запущена. Просто убедитесь, что вы завершаете пакетную часть с помощью EXIT или EXIT/B Тогда остальная часть скрипта может быть обычным vbs.

При необходимости вы можете даже иметь пакетную метку. :'Label является действительным комментарием vbs и допустимым ярлыком пакета.

Вот тривиальный гибридный сценарий. (снова с помощью <SUB> вместо встроенного символа Ctrl-Z)

::'<SUB>@cscript //nologo //e:vbscript "%~f0" & exit /b
WScript.Echo "Example of a hybrid VBS / batch file"

Обновление 2012-04-15

jeb нашел решение, которое позволяет избежать неудобного CTRL-Z, но он печатает ECHO OFF в начале и также устанавливает некоторые посторонние переменные.

Я нашел решение без CTRL-Z, которое устраняет посторонние переменные и проще понять.

Обычно специальные символы &, | , <, > и т.д. не работают после инструкции REM в пакете. Но специальные персонажи работают после REM. , Я нашел этот самородок информации по адресу http://www.dostips.com/forum/viewtopic.php?p=3500#p3500. Тест показывает, что REM. по-прежнему является действительным комментарием VBS. EDIT - основанный на комментарии jeb, безопаснее использовать REM^ (после каретки есть пробел).

Итак, вот тривиальный гибрид VBS/batch, использующий REM^ &. Единственным недостатком является то, что он печатает REM & в начале, тогда как решение jeb печатает ECHO OFF.

rem^ &@cscript //nologo //e:vbscript "%~f0" & exit /b
WScript.Echo "Example of a hybrid VBS / batch file"

Вот еще один тривиальный пример, демонстрирующий несколько командных команд, включая CALL для помеченной подпрограммы.

::' VBS/Batch Hybrid

::' --- Batch portion ---------
rem^ &@echo off
rem^ &call :'sub
rem^ &exit /b

:'sub
rem^ &echo begin batch
rem^ &cscript //nologo //e:vbscript "%~f0"
rem^ &echo end batch
rem^ &exit /b

'----- VBS portion ------------
wscript.echo "begin VBS"
wscript.echo "end VBS"
'wscript.quit(0)

Мне все еще нравится решение CTRL-Z, потому что оно устраняет все посторонние выходы.

ОБНОВЛЕНИЕ 2012-12-17

Том Лаведас опубликовал метод для удобного запуска динамического VBS из пакетного сценария в группах Google: нет гибридных сценариев VBS. В методе используется mshta.exe(Microsoft HTML Application Host).

Его оригинальное пакетное решение полагалось на внешний небольшой скрипт VBS.BAT для выполнения VBS в FOR/F. Я немного модифицировал синтаксис, чтобы сделать его удобным для встраивания непосредственно в любой заданный пакетный скрипт.

Это довольно медленно, но очень удобно. Он ограничен исполнением одной строки VBS.

VBS записывается нормально, за исключением того, что все кавычки должны быть удвоены: цитата, содержащая строку, должна быть записана как "", а кавычки внутри строки должны быть записаны как """". Обычно мини-скрипт выполняется внутри предложения IN() FOR/F. Он может выполняться напрямую, но только если stdout был перенаправлен или передан по каналам.

Он должен работать с любой ОС Windows с XP до тех пор, пока установлен IE.

@echo off
setlocal
:: Define simple batch "macros" to implement VBS within batch
set "vbsBegin=mshta vbscript:Execute("createobject(""scripting.filesystemobject"")"
set "vbsBegin=%vbsBegin%.GetStandardStream(1).write("
set ^"vbsEnd=):close"^)"

:: Get yesterday date
for /f %%Y in ('%vbsBegin% date-1 %vbsEnd%') do set Yesterday=%%Y
set Yesterday
pause
echo(

:: Get pi
for /f %%P in ('%vbsBegin% 4*atn(1) %vbsEnd%') do set PI=%%P
set PI
pause
echo(

set "var=name=value"
echo Before - %var%
:: Replace =
for /f "delims=" %%S in (
  '%vbsBegin% replace(""%var%"",""="","": "") %vbsEnd%'
) do set "var=%%S"
echo After  - %var%
pause
echo(

echo Extended ASCII:
for /l %%N in (0,1,255) do (

  %=  Get extended ASCII char, except can't work for 0x00, 0x0A.  =%
  %=  Quotes are only needed for 0x0D                             =%
  %=    Enclosing string quote must be coded as ""                =%
  %=    Internal string quote must be coded as """"               =%
  for /f delims^=^ eol^= %%C in (
    '%vbsBegin% """"""""+chr(%%N)+"""""""" %vbsEnd%'
  ) do set "char.%%N=%%~C"

  %=  Display result  =%
  if defined char.%%N (
    setlocal enableDelayedExpansion
    echo(   %%N: [ !char.%%N! ]
    endlocal
  ) else echo(   %%N: Doesn't work :(
)
pause
echo(

:: Executing the mini VBS script directly like the commented code below 
:: will not work because mshta fails unless stdout has been redirected
:: or piped.
::
::    %vbsBegin% ""Hello world"" %vbsEnd%
::

:: But this works because output has been piped
%vbsBegin% ""Hello world"" %vbsEnd% | findstr "^"
pause

ОБНОВЛЕНИЕ 2014-04-27

В DosTips есть большой сборник гибридов js/vbs/html/hta и химер в cmd/bat. Много хороших вещей от разных людей.

Внутри этой темы пользователь DosTips Liviu обнаружил красивое гибридное VBS/пакетное решение, которое использует WSF.

<!-- : Begin batch script
@echo off
cscript //nologo "%~f0?.wsf"
exit /b

----- Begin wsf script --->
<job><script language="VBScript">
  WScript.Echo "VBScript output called by batch"
</script></job>

Я думаю, что это решение является фантастическим. Секции партии и WSF четко разделены хорошими заголовками. Код партии абсолютно нормальный, без какого-либо нечетного синтаксиса. Единственное ограничение заключается в том, что пакетный код не может содержать -->.

Точно так же код VBS в WSF абсолютно нормальный. Единственное ограничение - код VBS не может содержать </script>.

Единственный риск - недокументированное использование "%~f0?.wsf" в качестве сценария для загрузки. Каким-то образом парсер правильно находит и загружает запущенный.BAT-скрипт "%~f0", а суффикс ?.wsf загадочно инструктирует CSCRIPT интерпретировать сценарий как WSF. Надеюсь, MicroSoft никогда не отключит эту функцию.

Поскольку решение использует WSF, пакетный скрипт может содержать любое количество независимых VBS, JScript или других заданий, которые могут быть выборочно вызваны. Каждая работа может даже использовать несколько языков.

<!-- : Begin batch script
@echo off
echo batch output
cscript //nologo "%~f0?.wsf" //job:JS
cscript //nologo "%~f0?.wsf" //job:VBS
exit /b

----- Begin wsf script --->
<package>
  <job id="JS">
    <script language="VBScript">
      sub vbsEcho()
        WScript.Echo "VBScript output called by JScript called by batch"
      end sub
    </script>
    <script language="JScript">
      WScript.Echo("JScript output called by batch");
      vbsEcho();
    </script>
  </job>
  <job id="VBS">
    <script language="JScript">
      function jsEcho() {
        WScript.Echo("JScript output called by VBScript called by batch");
      }
    </script>
    <script language="VBScript">
      WScript.Echo "VBScript output called by batch"
      call jsEcho
    </script>
  </job>
</package>

Ответ 2

EDIT: мой первый ответ был неправильным для VBScript, теперь моя следующая попытка...

Хорошая идея использовать CTRL-Z, но мне не нравятся управляющие символы в пакетном файле, так как это сложно скопировать и вставить их.
Это зависит от вашего браузера, вашего редактора, вашего...

Вы можете получить гибридный VBScript/Batch также с обычными символами и "нормальным" кодом.

:'VBS/Batch Hybrid
:
:
:'Makes the next line only visible for VBScript ^
a=1_
<1' echo off <nul 

set n=nothing' & goto :'batchCode & REM Jump to the batch code

'VBScript-Part
wscript.echo "VB-Start"
wscript.echo "vb-End"
wscript.quit(0)

'Batch part
:'batchCode
set n=n'& echo Batch-Start
set n=n'& echo two
set n=n'& echo Batch-End
set n=n'& cscript /nologo /E:vbscript vbTest.bat

Хитрость заключается в том, чтобы добавить каждую строку партии с помощью set n=n'&, она легальна для обоих, но vbs игнорирует остальную часть строки, только партия будет выполнять оставшуюся часть строки.

Другой вариант :'remark ^, это замечание для обоих, но для партии это замечание также следующая строка многострочным символом.
VbScript видит тогда a=1<1 остальная часть строки - это замечание '
Пакет видит только <1' echo off <nul, первая переадресация из 1' будет переопределяться вторым <nul, поэтому это приведет только к echo off < nul.

Единственная оставшаяся проблема заключается в том, что вы можете увидеть первый echo off, так как он не работает в пакетном режиме, чтобы использовать @ после перенаправления.

Для JScript существует более простое решение гибридных скриптов

Ответ 3

И моя попытка (сначала размещена здесь). Она похожа на решение jeb, но (по крайней мере, по мне) код более читабельный

:sub echo(str) :end sub
echo off
'>nul 2>&1|| copy /Y %windir%\System32\doskey.exe %windir%\System32\'.exe >nul

'& echo/ 
'& cscript /nologo /E:vbscript %~f0
'& echo/
'& echo BATCH: Translation is at best an ECHO.
'& echo/
'& pause
'& rem del /q "%windir%\System32\'.exe"
'& exit /b

WScript.Echo "VBScript: Remorse is the ECHO of a lost virtue."
WScript.Quit

И объяснение:

  • ' (одинарная кавычка) не является запрещенным символом как часть имени файла в окнах, поэтому нет проблем с файлом с именем '.exe
  • Есть несколько команд, заполненных окнами, которые ничего не делают без параметров командной строки. Самые быстрые (при отсутствии ничего) и большинство света в качестве размера (согласно моим тестам) - это subst.exe и doskey.exe
  • Итак, в первой строке я обрабатываю doskey.exe до '.exe (если он еще не существует) и затем продолжайте использовать его как '& (поскольку расширение .exe должно быть в % PATHEXT%). Это будет принято как комментарий в VBScript, а в пакете ничего не будет делать - просто будет продолжаться следующая команда на линии.

Конечно, есть некоторые недостатки. По крайней мере, для первого запуска в конечном итоге вам понадобятся права администратора, так как копирование в %windir%\System32\ может быть отклонено. Для надежности вы также можете использовать '>nul 2>&1|| copy /Y %windir%\System32\doskey.exe .\'.exe >nul, а затем в конце: '& rem del /q .\'.exe

С '.exe, оставшимся на вашем пути, вы могли бы непреднамеренно использовать его ненадлежащим образом, а выполнение в каждой строке ".exe" в конечном итоге могло бы снизить производительность.

.. И в командах частичной партии команды должны быть только в одной строке.

Обновление 9.10.13 (.. следуя приведенному выше соглашению)

Здесь еще один способ, который требует самопереименования командного файла:

@echo off
goto :skip_xml_comment
<!--
:skip_xml_comment

echo(
echo Echo from the batch
echo(

( ren %0 %0.wsf 
 cscript %0.wsf 
 ren %0.wsf %0 )


exit /b 0
-->

<package>
   <job id="vbs">
      <script language="VBScript">

         WScript.Echo "Echo from the VBS"

      </script>
   </job>
</package>

И короткое объяснение:

  • Когда пакет автоматически переименовывается и переименовывается со старым именем, и это делается в одной строке (или в скобках), script будет выполняться без ошибок. В этом случае добавляется также один вызов CSCRIPT.EXE с .WSF file.Self-переименование немного рискованно, но быстрее, чем временный файл.
  • Хост Windows script не заботится о элементах за пределами XML-данных, если нет специальных символов xml & < > ;, поэтому @echo off и goto можно использовать без забот. Но для обеспечения безопасности полезно размещать командные команды в разделе комментария xml (или CDATA). Чтобы избежать сообщений об ошибках из пакета, я пропустил <!-- с помощью goto здесь.
  • Перед закрытием пакета комментариев xml script завершается с помощью exit

Итак, хорошо, что нет необходимости писать весь пакетный код в командах с одной строкой, нет раздражающего echo off, jscript-код и различные задания могут быть добавлены в script. Также нет необходимости в специальных символах и создании дополнительных exe файлов, таких как '.exe

С другой стороны, самопереименование может быть рискованным.

Ответ 4

Я попытался собрать все решения в одном script на http://www.dostips.com/forum/viewtopic.php?p=37780#p37780.

Существует пакетная версия script, преобразующая наиболее популярные языки в пакетный файл (.js,.vbs,.ps1,.wsf,.hta и history.pl).

Он работает следующим образом:

    :: convert filename1.vbs to executable filename1.bat
    cmdize.bat filename1.vbs

Ответ 5

Существует очень простое решение этой проблемы. Просто используйте альтернативный поток данных (ADS) для хранения кода VBS. Чтобы просто объяснить это, ADS - это еще одно место, где вы можете хранить другие данные в одном файле, то есть файл состоит из его исходных данных и любого количества дополнительных ADS. Каждый ADS идентифицируется, отделяя свое имя от исходного имени файла через двоеточие. Например:

test.bat                    <- a file called "test.bat"
test.bat:script_.vbs        <- an ADS of file "test.bat" called "script_.vbs"

Вы можете найти более техническое и подробное описание ADS в Интернете, но сейчас важно упомянуть, что эта функция работает только на дисках NTFS. Теперь давайте посмотрим, как использовать эту функцию для решения этой конкретной проблемы.

Сначала создайте исходный пакетный файл обычным способом. Вы также можете запустить notepad.exe из командной строки следующим образом:

notepad test.bat

В моем примере я вставил следующие строки в test.bat:

@echo off
setlocal

For /f "delims=" %%i in ('Cscript //nologo "test.bat:script_.vbs" "Select a folder"') do Set "folder=%%i"

echo Result: "%folder%"

Обратите внимание на имя VBS script. После того, как test.bat был сохранен и Блокнот закрыт, создайте VBS script с помощью Блокнота, поэтому введите эту команду в командной строке:

notepad test.bat:script_.vbs

И создайте script обычным способом:

Dim objFolder, objShell
Set objShell = CreateObject("Shell.Application")
Set objFolder = objShell.BrowseForFolder(0, "Select a folder.", &H4000, 0)
If Not (objFolder Is Nothing) Then
   wscript.echo objFolder.Self.path
Else
   wscript.echo 0
End If

Сохраните этот файл и запустите test.bat. Оно работает! ;)

Вы можете просмотреть ADS файла test.bat с помощью переключателя /R в dir. Вы можете прочитать более подробное объяснение этого метода и его ограничений на этот пост.