是否有可能在不使用临时文件的情况下在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引擎将不会看到它,但批处理命令将运行。 只要确保您用EXIT
或EXIT /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
而解释:
- ' (单引号)不是作为窗口中文件名的一部分的禁止符号,所以没有一个名为
'.exe
- 有很less的命令与Windows压缩命令行参数什么都不做。最快(无所事事)和最轻的大小(根据我的testing)是
subst.exe
和doskey.exe
- 所以在第一行我正在处理
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>
而一个简短的解释:
- 当这个批次被自我重新命名并重新命名为旧名称,并且这是在一行(或括号内)完成的脚本将被执行没有错误。在这种情况下,也添加了一个
CSCRIPT.EXE
调用.WSF
文件。自重命名有一点风险,但比临时文件快。 - Windows脚本宿主对xml数据以外的项目不太在意,只要没有特殊的xml符号
& < > ;
所以@echo off
和goto
都可以使用而不用担心。 但是为了安全起见,把批处理命令放在xml注释(或CDATA)部分是很好的。为了避免批处理中的错误信息,我跳过了<!--
goto
这里。 - 在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。 你可以阅读这个方法的更广泛的解释和在这篇文章中的局限性。