是否可以在不编写临时文件的情况下在批处理中执行嵌入式VBScript?
#1 楼
注意-跳到该答案底部的UPDATE 2014-04-27部分以获取最佳解决方案。我以前认为答案是否定的。但是,随后DosTips用户Liviu发现,将
<SUB>
字符(Ctrl-Z,0x1A,十进制26)嵌入到批处理文件中时会产生bizare效果。如果函数有点像行终止符,那么在REM(或:::注释hack)之后的批处理命令如果在Ctrl-Z之前可以执行。 http://www.dostips.com/forum/viewtopic.php?p=13160#p13160 已在XP Home Edition sp3、64位Vista Home Premium sp2和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
终止批次部分即可。然后,脚本的其余部分可以看起来很普通。如果需要,您甚至可以具有批处理标签。
:'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 /批处理混合。唯一的缺点是它在开始时打印REM &
,而jeb的解决方案在开始时打印ECHO OFF
。这是另一个简单的示例,它演示了多个批处理命令,包括从CALL到CALL的调用。带标签的子例程。rem^ &@cscript //nologo //e:vbscript "%~f0" & exit /b
WScript.Echo "Example of a hybrid VBS / batch file"
我仍然喜欢CTRL-Z解决方案,因为它消除了所有无关的输出。
UPDATE 2012-12- 17
Tom Lavedas在Google网上论坛发布了一种从批处理脚本方便地运行动态VBS的方法:没有文件VBS混合脚本。该方法使用mshta.exe(Microsoft HTML应用程序主机)。
他的原始批处理解决方案依赖于外部小型VBS.BAT脚本在FOR / F中执行VBS。我对语法进行了少许修改,以方便直接嵌入任何给定的批处理脚本中。
速度很慢,但是非常方便。只能执行一行VBS。
VBS正常编写,除了所有引号必须加倍:包含字符串的引号必须写为
""
,并且字符串内部的引号必须被写为""""
。通常,迷你脚本是在FOR / F的IN()子句中执行的。它可以直接执行,但是仅当stdout已被重定向或通过管道传输时。只要安装了IE,它就可以在XP以后的任何Windows操作系统上运行。
::' 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)
更新2014-04-27
在DosTips上,有大量的js / vbs / html / hta混合体和cmd / bat中的嵌合体。来自不同人群的许多好东西。
在该线程中,DosTips用户Liviu发现了一种使用WSF的精美的VBS /批处理混合解决方案。
@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
我认为这个解决方案很棒。批处理和WSF部分明显由漂亮的标题分开。批处理代码绝对是正常的,没有任何奇怪的语法。唯一的限制是批处理代码不能包含
-->
。类似地,WSF中的VBS代码也是绝对正常的。唯一的限制是VBS代码不能包含</script>
。唯一的风险是无证使用
"%~f0?.wsf"
作为要加载的脚本。解析器以某种方式正确地找到并加载了正在运行的.BAT脚本"%~f0"
,后缀?.wsf
神秘地指示CSCRIPT将脚本解释为WSF。希望MicroSoft永远不会禁用该“功能”。由于该解决方案使用WSF,所以批处理脚本可以包含任意数量的独立VBS,JScript或可以有选择地调用的其他作业。每个工作甚至可以使用多种语言。
<!-- : 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>
评论
REM的第二种解决方案。也不错,但有缺点。如果存在名称为REM(无扩展名)的文件,则为REM。结果变成找不到文件错误。但是您可以使用REM ^:相反,这似乎总是可行的。 (也是REM ^,REM ^ ;、 REM ^)
–杰布
2012-04-15 22:44
@jeb-在我看来,REM:没有插入符号就永远不会失败,这是最简单的。还是有一种晦涩的情况会失败?
– dbenham
13年11月1日在22:12
@Paul-信不信由你,那行是有效的批次标签。 CMD.EXE充满了这样的特质。冒号之前的字符是少数可以出现在批处理标签之前的字符-杰布发现这些东西并将其张贴在DosTips上的某个位置。如果您愿意钻进兔子洞,则可以在dostips.com/forum/viewtopic.php?f=3&t=3803上阅读有关疯狂标签规则的信息
– dbenham
2014年12月28日在18:28
没错,REM ^失败。但是REM ^,REM ^;以及REM ^:似乎很安全,我找不到它们失败的方法,并且仍然可以在同一行中使用&
–杰布
16年1月14日上午10:57
@Paul-关于12月28日有关<!-的评论::开始...:该行并不是真正可调用的标签。 <!-被视为重定向输入,并且重定向可以出现在一行的任何地方。但是,解析器将其移动到内存的末尾,使得行的开始看起来像`:Begin ...`,它被解析为标签,因此该行不会执行(并且重定向永远不会发生)。
– dbenham
17/12/30在4:44
#2 楼
编辑:对于VBScript,我的第一个答案是错误的,现在我下一次尝试...使用CTRL-Z的好主意,但我不喜欢批处理文件中的控制字符,因为这是有问题的复制并粘贴它们。
取决于您的浏览器,编辑器,...
,您还可以获得带有普通字符和“普通”代码的混合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'&
,这对于两者都是合法的,但是vb将忽略该行的其余部分,只有批处理将执行其余部分另一个变体是
:'remark ^
,这是对这两者的说明,但是对于批处理,它也用多行字符也说明了下一行。VbScript会看到
a=1<1
的其余部分。该行是备注'
该批处理仅看到
<1' echo off <nul
,来自1'
的第一个重定向将被第二个<nul
覆盖,因此结果仅是echo off < nul
。 唯一剩下的问题是您可以看到第一个
echo off
,因为重定向后无法批量使用@
。对于JScript,存在一个更简单的方法解决方案混合脚本
评论
我看不出这对VBscript有什么帮助。 Jscript和VBscript具有完全不同的语法。真正的问题是VBscript缺少多行注释。如果单个批处理行暴露在脚本中的任何位置,则VBscript不会编译。
– dbenham
2012年1月31日12:15
您是对的,之前对vbscript的思考不够充分,我将答案更改为有用的变体
–杰布
2012年2月1日下午6:31
+1辉煌。可以通过在ECHO OFF之后添加&SETLOCAL来改进。我也将n = nothing移至第二行,并使用/ b出口将批次移至顶部,但这很漂亮。我仍然更喜欢Ctrl-Z解决方案,因为它支持@ECHO OFF,并且因为它没有设置任何无关的变量。丑陋的控制字符与不需要的ECHO OFF输出-我们每个人都必须选择毒药:-)
– dbenham
2012年2月2日,下午3:12
#3 楼
我试图将所有解决方案组装在一个脚本中,网址为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
#4 楼
这个问题有一个非常简单的解决方案。只需使用备用数据流(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"
您可能会在Web上找到有关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
。有用! ;)
您可以通过
test.bat
命令中的/R
开关查看dir
文件的ADS。您可以在这篇文章中阅读有关此方法及其局限性的更广泛的解释。评论
现在,我发现这很有趣。由于某种原因,偷偷摸摸。虽然我想不起来立即使用,但我将尝试将其归档以备将来使用。
– NapkinBob
18年4月11日在14:59
仅适用于NTFS!
– SachaDee
18年4月13日在14:09
@SachaDee:是的。我已经在第二段的第一句中指出:您可能会在Web上找到有关ADS的更多技术性和详细描述,但是现在重要的是要提到此功能仅适用于NTFS磁盘。
–Aacini
18年4月13日在14:12
糟糕!没看到抱歉!
– SachaDee
18年4月13日在14:14
@TheBlastOne:不,你错了。这里只有一个文件,一个扩展名为.bat的文件!
–Aacini
18年7月20日在20:01
#5 楼
我的尝试(首先发布在这里)。它类似于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
以及解释:在Windows中,作为文件名的一部分,
'(单引号)不是禁止的符号,因此,有一个名为
'.exe
的文件没有问题。Windows打包的命令很少,没有命令行参数就什么也不做。最快(什么也不做)和最轻的大小(根据我的测试)是
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 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>
并简短说明:
自重命名该批处理时并用旧名称重新命名,这将在一行(或括号)中完成,脚本将无错误地执行。在这种情况下,还添加了一次调用
CSCRIPT.EXE
和.WSF
文件的功能。冒险,但更快n临时文件。只要没有特殊的xml符号
& < > ;
,Windows脚本宿主就不会在意xml数据之外的项目,因此可以轻松使用@echo off
和goto
。但是为了安全起见,最好将批处理命令放在xml注释(或CDATA)部分中。为避免该批处理中出现错误消息,我在这里跳过了<!--
和goto
。在关闭xml批处理脚本之前,以
exit
终止所以好事情是不需要在单行命令中编写所有批处理代码,
不会显示烦人的
echo off
,可以向其中添加jscript代码和其他作业也不需要特殊符号和创建其他exe文件,例如'.exe
另一方面,自我重新命名可能会带来风险。
#6 楼
我没有VB.NET对VBScript的精确计数,但是它绝对是带有某些C#风格的VisualBasic代码。在
msbuild
版本中,有一个称为内联任务的东西,允许您加载.net代码。进入内存并执行它。而且它可以用与wsf相似的方式嵌入到批处理文件中:<!-- :
@echo off
echo -^- FROM BATCH
set "CMD_ARGS=%*"
::::::::::::::::: Starting VB.NET code :::::::::::::::::::::::
::
:: searching for msbuild location
for /r "%SystemRoot%\Microsoft.NET\Framework\" %%# in ("*msbuild.exe") do set "msb=%%#"
if not defined msb (
echo no .net framework installed
exit /b 10
)
:::::::::: calling msbuid :::::::::
call %msb% /nologo /noconsolelogger "%~dpsfnx0"
::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
exit /b %errorlevel%
-->
<Project ToolsVersion="$(MSBuildToolsVersion)" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="T">
<T/>
</Target>
<UsingTask
TaskName="T"
TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v$(MSBuildToolsVersion).dll" >
<Task>
<!-- include this for UI programs -->
<!-- Reference Include="$(MSBuildToolsPath)\System.Windows.Forms.dll"/ -->
<Using Namespace="System"/>
<Code Type="Fragment" Language="vb">
<![CDATA[
'''' VB CODE STARTS HERE ''''
Console.WriteLine("-- FROM VB.NET"):
'''''''''''''''''''''''''''''
]]>
</Code>
</Task>
</UsingTask>
</Project>
它确实很冗长,尽管xml语法允许将几乎所有内容都放在一行中(如果有人愿意的话)(但是我想出于教育原因,最好将此示例保留为该示例)。批处理代码不能包含
--
,因此出于健壮性的考虑,此部分也可以放在CDATA中。将VB.NET嵌入批处理而无需临时文件的其他方法是借助powershell及其类型定义cmdlet:
<# : batch portion
@echo off & setlocal
set "CMD_ARGS=%~1"
powershell -noprofile "iex (${%~f0} | out-string)"
goto :EOF
: end batch / begin powershell #>
param($psArg1 = $env:psArg1)
$VB = @"
Imports System
namespace VB
Public Class VB
Public Shared Sub Print()
Console.WriteLine("PRINTED BY VB")
End Sub
End Class
End Namespace
"@
Add-Type -TypeDefinition $VB -Language VisualBasic
[VB.VB]::Print()
评论
那么PowerShell + VBScript呢? -> stackoverflow.com/questions/63514534 / ...