长期以来,人们一直在批处理文件中嵌入和执行VBScript。但是,在我看到的所有已发表的解决方案(即在最初提出此问题时)中,都需要编写临时VBS文件。例如:在Windows批处理文件中嵌入VBScript。
是否有可能在不编写临时文件的情况下执行批处理中嵌入的VBScript?
长期以来,人们一直在批处理文件中嵌入和执行VBScript。但是,在我看到的所有已发表的解决方案(即在最初提出此问题时)中,都需要编写临时VBS文件。例如:在Windows批处理文件中嵌入VBScript。
是否有可能在不编写临时文件的情况下执行批处理中嵌入的VBScript?
<SUB>
字符(Ctrl-Z,0x1A,十进制26)嵌入批处理文件时,它会产生奇怪的影响。如果在Ctrl-Z之前加上REM(或:: remark hack),则可以使后续批处理命令执行,就像行终止符一样。http://www.dostips.com/forum/viewtopic.php?p=13160#p13160
这已在XP Home Edition sp3、Vista Home Premium sp2 64位和Vista Enterprise sp2 32位上得到确认。我假设它也适用于其他Windows版本。<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
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注释。编辑 - 根据jeb的评论,更安全的做法是使用REM^
(插入符号后面有一个空格)。
REM^ &
的简单VBS/batch混合程序。唯一的缺点是它会在开头打印REM &
,而jeb的解决方案会打印ECHO OFF
。rem^ &@cscript //nologo //e:vbscript "%~f0" & exit /b
WScript.Echo "Example of a hybrid VBS / batch file"
::' 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)
""
,而用于字符串内部的引号必须写为""""
。通常,在FOR / F的IN()子句中执行迷你脚本。如果已重定向或管道stdout,则可以直接执行它。@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用户Liviu发现了一个漂亮的VBS/batch混合体解决方案,使用了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>
-->
。</script>
。"%~f0?.wsf"
作为要加载的脚本。不知何故,解析器可以正确地找到和加载正在运行的.BAT脚本"%~f0"
,并且?.wsf
后缀神秘地指示CSCRIPT将脚本解释为WSF。希望微软永远不会禁用那个“功能”。<!-- : 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>
REM.
也不错,但有一个缺点。如果存在一个名为 REM
(无扩展名)的文件,REM.
会导致一个 文件未找到
的错误。但是你可以改用 REM^:
,这似乎总是有效的。(也可以用 REM^,
、REM^;
、REM^
) - jebREM:
永远不会失败,并且是最简单的。或者有一种模糊的情况会导致它失败吗? - dbenhamREM^
会失败。但是REM^,
, REM^;
以及REM^:
似乎是安全的,我找不到它们失败的方法,而且它们仍然可以在同一行中使用&
。 - jeb<!-- : Begin...
:这一行实际上不是一个可调用的标签。<!--
被视为重定向输入,并且重定向可以出现在一行的任何位置。然而,解析器在内存中将其移动到末尾,这使得该行开头看起来像是 : Begin...
,被解析为标签,因此该行不会被执行(也不会发生重定向)。 - dbenham编辑:我的第一个答案对于 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
,因为在重定向后使用@
无法在批处理中工作。&SETLOCAL
可能会有所改进。我还会将 n=nothing 移到第二行,并将批处理移动到顶部与 exit /b,但这只是外观上的问题。我仍然更喜欢 Ctrl-Z 的解决方案,因为它支持 @ECHO OFF,并且不设置任何多余的变量。丑陋的控制字符 vs. 不需要的 ECHO OFF 输出 - 我们每个人都必须选择自己的毒药 :-) - dbenham这个问题有一个非常简单的解决方案。只需使用备用数据流(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脚本的名称。在保存了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
。它可以正常工作!;)
您可以通过dir
命令中的/R
开关来查看test.bat
文件的ADS。您可以在此帖子中阅读更详细的说明以及该方法的限制。
.bat
的那个! - Aacini:: 将filename1.vbs转换为可执行的filename1.bat cmdize.bat filename1.vbs
这是我的尝试(此处首次发布)。它类似于jeb的解决方案,但是(至少在我看来)代码更易读:
:sub echo(str) :end sub
echo off
'>nul 2>&1|| copy /Y %windir%\System32\doskey.exe ".\'.exe" >nul
'& echo/
'& cscript /nologo /E:vbscript %~f0 %*
'& echo/
'& echo BATCH: Translation is at best an ECHO.
'& echo/
'& pause
'& rem del /q ".\'.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
,您可能会不正确地使用它,并且每行代码的执行最终可能会降低性能。
..而且,在批处理部分,命令必须只在一行中。
更新9.10.13(..遵循上述约定)
这里还有一种需要批处理文件自我重命名的方法:
@echo off
goto :skip_xml_comment
<!--
:skip_xml_comment
echo Echo from the batch
::start vbs code
(
ren %0 %0.wsf
cscript %0.wsf //nologo %*
ren %0.wsf %0
)
exit /b 0
-->
<package>
<job id="vbs">
<script language="VBScript">
WScript.Echo "Echo from the VBS"
</script>
</job>
</package>
并附上简短的解释:
.WSF
}}文件的CSCRIPT.EXE
。自我重命名有点冒险,但比使用临时文件更快。& < > ;
,因此可以放心使用@echo off
和goto
。但为了安全起见,最好将批处理命令放在xml注释(或CDATA)部分中。为了避免批处理出现错误消息,在这里我跳过了<!--
与goto
。exit
终止批处理脚本。好处是不需要在单行命令中编写所有批处理代码,没有烦人的echo off
显示,可以将jscript代码和不同的作业添加到脚本中。此外,也不需要特殊符号和创建额外的exe文件,如'.exe
另一方面,自我重命名可能存在风险。