是否有可能在不使用临时文件的情况下在batch file中embedded和执行VBScript?

人们已经在batch file中embedded和执行VBScript很长一段时间了。 但是我所看到的所有已发表的解决scheme(当时这个问题最初都是这样提出的)都涉及到写一个临时的VBS文件。 例如: 在Windowsbatch file中embeddedVBScript 。

是否可以批量执行embedded式VBScript而不写入临时文件?

注 – 跳转 到此答案底部 UPDATE 2014-04-27 部分以获得最佳解决scheme。

我曾经认为答案是否定的。 但是,然后DosTips用户Liviu发现,embedded在batch file中时, <SUB>字符(Ctrl-Z,0x1A,十进制26)具有bizare效果。 如果函数有点类似于行结束符,那么在REM(或者:: remark)后面的批处理命令可能被执行(如果它们前面有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版本。

注意下面的代码应该embeddedCtrl-Z字符。 我发誓我曾经在IE8浏览过的网站上看到他们。 但他们似乎已经失去了这个职位,我不知道如何发布他们了。 我用string<SUB>replace了字符

 ::<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引擎将不会看到它,但批处理命令将运行。 只要确保您用EXITEXIT /B终止批量部分。 那么脚本的其余部分可以是正常的vbs。

如果需要的话,你甚至可以有一个批次标签。 :'Label既是有效的vbs注释又是有效的批量标签。

这是一个简单的混合脚本。 (再次用<SUB>代替embedded的Ctrl-Z字符)

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

更新2012-04-15

jebfind了一个解决scheme,避免了令人尴尬的CTRL-Z,但它在开始时打印出ECHO OFF,并设置了一些无关的variables。

我find了一个没有CTRL-Z的解决scheme,消除了无关的variables,并且更容易理解。

通常的特殊字符&|<>等在批处理REM语句后不起作用。 但特殊字符在REM.之后工作REM. 。 我在http://www.dostips.com/forum/viewtopic.php?p=3500#p3500find了这个信息块。; 一个testing显示REM. 仍然是一个有效的VBS评论。 编辑 – 根据jeb的评论,使用REM^是安全的(插入后有一个空格)。

所以这里是使用REM^ &一个微不足道的VBS /批量混合。 唯一的缺点是它在开始时打印REM &而jeb的解决scheme打印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解决scheme,因为它消除了所有无关的输出。

更新2012-12-17

Tom Lavedas发布了一个方法,可以通过Google Groups的批处理脚本方便地运行dynamicVBS: 无文件VBS混合脚本 。 该方法使用mshta.exe(Microsoft HTML应用程序主机)。

他的原始批处理解决scheme依靠一个外部的小VBS.BAT脚本在FOR / F中执行VBS。 我稍微修改了这个语法,以便直接embedded到任何给定的批处理脚本中。

这是相当缓慢,但非常方便。 它仅限于执行一行VBS。

VBS是正常写入的,除了所有的引号必须加倍:包含string的引号必须写成"" ,引号内部的string必须写成"""" 。 通常,迷你脚本是在FOR / F的IN()子句中执行的。 它可以直接执行,但只有当stdout已被redirect或pipe道。

只要安装了IE,就可以在XP以后的任何Windows操作系统上运行。

 @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上有一个在cmd / bat中的js / vbs / html / hta混合体和嵌合体的大纲。 很多来自不同人的好东西。

在这个线程中,DosTips用户Liviu发现了一个使用WSF的漂亮的VBS /批量混合解决scheme。

 <!-- : 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> 

我认为这个解决scheme太棒了。 批处理和WSF部分显然是由好的标题分隔的。 批处理代码是绝对正常的,没有任何奇怪的语法。 唯一的限制是批处理代码不能包含-->

同样的,WSF内的VBS代码是绝对正常的。 唯一的限制是VBS代码不能包含</script>

唯一的风险是作为加载脚本的''%〜f0?.wsf''的无证使用。 parsing器以某种方式正确地find并加载正在运行的.BAT脚本"%~f0" ,并且?.wsf后缀神秘地指示CSCRIPT将脚本解释为WSF。 希望MicroSoft不会禁用这个“function”。

由于解决scheme使用WSF,批处理脚本可以包含任意数量的独立VBS,JScript或其他可以select性调用的作业。 每个工作甚至可以使用多种语言。

 <!-- : 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> 

编辑:我的第一个答案是错误的VBScript,现在我的下一个尝试…

不错的主意使用CTRL-Z,但我不喜欢batch file中的控制字符,因为复制和粘贴它们是有问题的。
这取决于你的浏览器,你的编辑器,你的…

你可以得到一个混合的VBScript /批处理也与普通的字符,和“正常”的代码。

 :'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'redirect将被第二个<nul覆盖,所以结果只能echo off < nul

唯一剩下的问题是,你可以看到第一个echo off ,因为在redirect之后,它不能批量使用@

对于JScript存在一个更简单的解决scheme混合脚本

我试图在http://www.dostips.com/forum/viewtopic.php?p=37780#p37780的一个脚本中汇编所有解决scheme。;

批处理脚本将最stream行的语言转换为batch file( .js.vbs.ps1.wsf.hta和历史.pl )。

它的工作原理如下:


     ::将filename1.vbs转换为可执行文件filename1.bat
     cmdize.bat filename1.vbs

而我的尝试( 首先在这里发布 )。它类似jeb的解决scheme,但(至less根据我)代码是更具可读性:

 :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. ' (单引号)不是作为窗口中文件名的一部分的禁止符号,所以没有一个名为'.exe
  2. 有很less的命令与Windows压缩命令行参数什么都不做。最快(无所事事)和最轻的大小(根据我的testing)是subst.exedoskey.exe
  3. 所以在第一行我正在处理doskey.exe'.exe (如果它不存在),然后继续使用它作为'& (因为.exe扩展名应该在%PATHEXT% )。这将是作为VBScript中的一个注释,批处理将不会执行任何操作 – 只要继续执行下一个命令即可。

当然也有一些缺陷。至less第一次运行最终你需要pipe理员权限,因为%windir%\System32\的应对可能被拒绝。为了鲁棒性,你也可以使用'>nul 2>&1|| copy /Y %windir%\System32\doskey.exe .\'.exe >nul '>nul 2>&1|| copy /Y %windir%\System32\doskey.exe .\'.exe >nul然后在最后: '& rem del /q .\'.exe

如果'.exe留在你的path中,你可能无意中以不正确的方式使用它,并且'.exe'每行的执行最终会降低性能。

而批处理命令只能在一行中。

更新9.10.13(按照上述惯例)

还有一种方法需要对batch file进行自我重命名:

 @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文件。自重命名有一点风险,但比临时文件快。
  2. Windows脚本宿主对xml数据以外的项目不太在意,只要没有特殊的xml符号& < > ; 所以@echo offgoto都可以使用而不用担心。 但是为了安全起见,把批处理命令放在xml注释(或CDATA)部分是很好的。为了避免批处理中的错误信息,我跳过了<!-- goto这里。
  3. 在closuresxml注释之前,批处理脚本以exit结束

所以好的是,不需要在单行命令中编写所有的批处理代码,不会显示烦人的echo off显,jscript代码和不同的工作可以添加到脚本中。也不需要特殊的符号和创build额外的exe文件像'.exe

另一方面,自我更名可能是有风险的。

这个问题有一个非常简单的解决scheme。 只需使用备用数据stream (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" 

您可能会在networking上find更多关于ADS的技术性和广泛的描述,但是现在必须提及的是,此function适用于NTFS磁盘。 现在,让我们看看如何使用这个function来解决这个特定的问题。

首先,以通常的方式创build原始的batch file。 你也可以这样从命令行启动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并closures记事本之后,使用记事本创buildVBS脚本,所以在命令行中input以下命令:

 notepad test.bat:script_.vbs 

并以通常的方式创build脚本:

 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 。 有用! ;)

您可以通过dir命令中的/R开关查看test.bat文件的ADS。 你可以阅读这个方法的更广泛的解释和在这篇文章中的局限性。