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

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

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

Ответы [ 5 ]

52 голосов
/ 31 января 2012

Примечание. Перейдите к разделу ОБНОВЛЕНИЕ 2014-04-27 в нижней части этого ответа, чтобы найти лучшее решение.

Раньше я думал, что ответ был отрицательным.Но затем пользователь DosTips Ливиу обнаружил, что символ <SUB> (Ctrl-Z, 0x1A, десятичное число 26) имеет причудливые эффекты при внедрении в пакетный файл.Если функции в некоторой степени похожи на терминатор строки, то есть возможно выполнение пакетных команд, следующих за REM (или хаком :: notes), если им предшествует Ctrl-Zhttp://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

Это ключ к успешному пакетному / 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

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

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

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

Его оригинальное пакетное решение основывалось на внешнем небольшом сценарии 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's 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, Ливиу, обнаружил красивое 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>

Я думаю, что это фантастическое решение.Секции batch и 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>
11 голосов
/ 31 января 2012

РЕДАКТИРОВАТЬ: мой первый ответ был неправильным для 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 голосов
/ 06 ноября 2014

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

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

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


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

3 голосов
/ 18 мая 2013

И моя попытка ( впервые опубликована здесь ). Она напоминает решение 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

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

  1. ' (одинарная кавычка) не является запрещенным символом как часть имени файла в Windows, поэтому нет проблем с файлом '.exe
  2. Есть несколько команд, заполненных окнами, которые ничего не делают без параметров командной строки. Самые быстрые (бездействующие) и наиболее легкие по размеру (согласно моим тестам) - subst.exe и doskey.exe
  3. Итак, в первой строке я копирую 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>

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

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

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

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

2 голосов
/ 16 марта 2017

Существует очень простое решение этой проблемы. Просто используйте Альтернативный поток данных (ADS) для хранения кода VBS. Проще говоря, ADS - это другое место , где вы можете хранить другие данные в том же файле , то есть файл состоит из его исходных данных плюс любое число * 1007. * дополнительные объявления . Каждый 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. После сохранения test.bat и закрытия Блокнота создайте сценарий VBS с помощью Блокнота, поэтому введите эту команду в командной строке:

notepad test.bat:script_.vbs

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

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. Вы можете прочитать более подробное объяснение этого метода и его ограничений в этом посте .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...