我的问题与这个问题有关。 我有几个需要从batch file执行的一系列操作,我想将它们build模为函数,并从一个主序列中调用。 从上面的问题来看,很明显,我可以用调用语法来做到这一点
call:myDosFunc
我的问题是,我可以把所有这些function放在一个单独的batch file(functions.bat),并以某种方式“包含”在主batch file中,并调用它们? 另一个select是利用调用main.bat中的functions.bat和调用syntaxt的可能性,但我不确定是否可以用特定的函数调用它,而不是执行整个batch file。
总之,我正在寻找类似于C编程世界的东西,其中我的函数驻留在一个DLL中,主程序仅包含高级逻辑并从DLL调用函数。
这是一个简单的例子,说明如何做。
调用函数脚本的函数名称为第一个参数,函数参数为arg2,arg3,…
假设它被正确调用,脚本移动参数并执行GOTO到原始的arg1。 然后函数的参数以新的arg1开始。 这意味着您可以将已经编写好的程序放在实用程序中,而无需担心调整参数号。
如果未提供函数参数,或者脚本中的函数参数与有效标签不匹配,则该脚本会提供错误。
@echo off if "%~1" neq "" ( 2>nul >nul findstr /rc:"^ *:%~1\>" "%~f0" && ( shift /1 goto %1 ) || ( >&2 echo ERROR: routine %~1 not found ) ) else >&2 echo ERROR: missing routine exit /b :test1 echo executing :test1 echo arg1 = %1 exit /b :test2 echo executing :test2 echo arg1 = %1 echo arg2 = %2 exit /b :test3 echo executing :test3 echo arg1 = %1 echo arg2 = %2 echo arg3 = %3 exit /b
我更喜欢上面使用的GOTO方法。 另一个选择是使用CALL,正如Thomas在他的回答中所做的那样。
有关使用CALL技术的批处理函数有用库的工作示例,请参见CHARLIB.BAT ,这是用于处理批处理文件中的字符和字符串的例程库。 显示图书馆发展的线程可以在这里找到
几年前我写了CharLib.bat。 如果我今天写,我可能会用GOTO而不是CALL。
引入CALL的问题在于,在将字符串文字作为参数传递时会产生问题。 额外的CALL意味着包含%
的字符串文字必须有%
一倍额外的时间。 这也意味着像&
和|
这样的没有引号的毒药字符 将需要额外的时间逃脱。 这两个问题可以由来电者解决。 但真正的问题是,每个CALL加倍引号插入: "^"
变成"^^"
。 解决插入符号问题的方法并不好。
额外的CALL的问题不影响CharLib.bat,因为字符串值通过引用(变量名称)而不是字符串文字传递。
使用GOTO和SHIFT / 1的唯一缺点是不能使用%0
来获取当前正在执行的例程的名称。 我可以在不使用/ 1的情况下使用SHIFT,但是在例程中不能使用%~f0
来获取正在执行的批处理文件的完整路径。
我认为在一个批处理文件的开头的路由功能并不难看。 你可以在“ libbatch.cmd ”的开头使用这样的东西
call:%* exit/b :func1 [do something] exit/b :func2 [do something else] exit/b
现在你可以从另一个批次调用func2:
call libbatch.cmd func2 params1 param2 ... paramN
这也保留了由func2“抛出”的错误级别(退出/返回当前错误级别)。 随着第二个电话而不是转到你确保“%1”==“param1”而不是func2。 如果标签不存在,调用将不会终止批处理文件,它只是将errorlevel设置为1,并将错误消息放置到2(errorout),该错误消息可能会被重定向到nul。
说明:%*包含所有参数,因此在该示例中,第一行转换为:
call:func2 params1 param2 ... paramN
你可以使用这种格式 – 并像这样启动它:
call mybat :function4 parameternumber2 parameternumber3
这将是使用图书馆的一种方式
@echo off goto %1 :function1 REM code here - recursion and subroutines will complicate the library REM use random names for any temp files, and check if they are in use - else pick a different random name goto :eof :function2 REM code here - recursion and subroutines will complicate the library REM use random names for any temp files, and check if they are in use - else pick a different random name goto :eof :function3 REM code here - recursion and subroutines will complicate the library REM use random names for any temp files, and check if they are in use - else pick a different random name goto :eof :function4 REM code here - recursion and subroutines will complicate the library REM use random names for any temp files, and check if they are in use - else pick a different random name goto :eof
您可以使用一个有趣的技巧来避免其他方法在尝试使主函数对库函数可用时的大部分问题,并且速度更快。 使用这个技巧的唯一必要条件是:
技巧在于运行批处理文件的“切换上下文” ,库文件成为正在运行的批处理文件; 这样,库文件中的所有功能都可以用于主代码块而不需要额外的处理。 当然,运行的批处理文件的“上下文”必须在代码块结束之前切换回主文件。
“切换上下文”的方法是使用与正在运行的主文件相同的名称重命名库文件(并将主文件重命名为另一个名称)。 例如:
( rem Switch the context to the library file ren main.bat orig-main.bat ren library.bat main.bat rem From this point on, any library function can be called . . . . rem Switch back the context to the original one ren main.bat library.bat ren orig-main.bat main.bat )
编辑 : 添加工作示例
我从屏幕上复制下面的例子。 在Windows 8中测试,但我也在Win XP中使用这种方法:
C:\Users\Antonio\Documents\test >type main.bat @echo off ( rem Switch the context to the library file ren main.bat orig-main.bat ren library.bat main.bat rem From this point on, any library function can be called, for example: echo I am Main, calling libFunc: call :libFunc param1 echo Back in Main rem Switch back the context to the original one ren main.bat library.bat ren orig-main.bat main.bat ) C:\Users\Antonio\Documents\test >type library.bat :libFunc echo I am libFunc function in library.bat file echo My parameter: %1 exit /B C:\Users\Antonio\Documents\test >main I am Main, calling libFunc: I am libFunc function in library.bat file My parameter: param1 Back in Main
我不确定原始问题的上下文,但这可能是使用VBScript或WPS切换到WSH或其他控制台脚本以外的批处理文件的情况。 我会回答原来的问题,但首先..一点背景和理解..
DOS和Windows的命令行/控制台模式通常是COMMAND.COM或CMD.EXE,它不适合脚本/编程逻辑。 相反,它们适用于执行命令和程序,并且将批处理文件添加到常用命令序列中以包装在单个命令中。 例如,你可能有一个你玩的旧的DOS游戏,每次都需要下面的命令,所以它被打包成一个批处理文件:
@EHO OFF @REM Load the VESA driver fix.. VESAFIX.EXE @REM Load the joystick driver.. JOYSTICK.COM @REM Now run the game RUNGAME.EXE
许多人倾向于将整个批处理文件视为一个原子单位 – 但事实并非如此。 命令解释程序(COMMAND.COM或CMD.EXE)只会像您一样手动输入这些行,每次运行批处理文件。 它实际上没有固定的词法和概念,就像一个普通的编程/脚本语言一样 – 也就是说,它不会像调用堆栈等那样维护额外的元数据。 它所维护的只是更多的添加,而不是从一开始就内置到批处理文件中。
然而,一旦你在思维上转换了一些思路,你就可以使用各种技巧和技巧来模拟更强大的脚本/编程语言, 但是你仍然必须记住批处理文件仍然会受到限制。
无论如何,使用批处理文件库的一种技术是创建一个批处理文件,其中第一个参数用于指示哪个函数被调用:
CALL BATLIB.BAT FunctionName Parameter1 Parameter2 ...
如果在编写这个图书馆的时候,这个工作就足够了,所以它会知道跳过第一个参数,等等。
在Windows系统中使用更现代的CMD.EXE版本允许在CALL语法中使用“:labels”,如果你想限制参数范围(它允许你使用%*作为“所有参数” ,例如),像这样:
CALL :LABEL Parameter1 Paramater2 ...
(从同一批处理文件或…)
CALL BATLIB.BAT :LABEL Parameter1 Parameter2 ...
关于这个的一些注意事项虽然..在第一种形式中,LABEL必须已经在当前的批处理文件中。 它将在CMD.EXE内创建一个新的“批处理上下文”,其中%*,%1,%2等与参数匹配。 但是,您还必须提供某种返回/退出逻辑,以从该上下文返回/退出到调用上下文。
在第二种形式中,CMD.EXE并不真正意识到你正在传递一个标签,所以你的批处理文件库将不得不期待它并处理它:
@ECHO OFF CALL %*
这是有效的,因为命令解释程序甚至在尝试解析CALL命令之前替换%*,所以在变量扩展之后,CALL命令会看到:LABEL就像硬编码一样。 这也造成了CMD.EXE创建另一个批处理上下文的情况,所以你必须确保从这个上下文返回/退出两次:一次为当前的库上下文,再次返回原来的CALL。
还有其他的方法来做批处理文件库,混合和匹配上述技术,或者使用更复杂的逻辑,使用GOTO等等。 这实际上是一个如此复杂的话题,在这个话题上有很多的书,比我想在这里输入一个简单的答案要多得多!
到目前为止,我大多忽略了其他的问题,如果CALL标签不存在,该怎么办? 如何处理? 那么环境变量扩展呢? 什么时候发生? 你怎么能防止它发生得太快? 那么在参数/参数中使用特殊的DOS字符呢? 例如,解释器如何看到如下行:CALL:ProcessPath%PATH%? (答案是CMD.EXE _ 在处理CALL命令之前 替换了整个 %PATH% ,如果你的路径中有空格可能会导致CALL处理整个事件Windows'%PATH%变量是.. C:\ Program Files ..例如..)
正如你所看到的,事情变得越来越复杂和凌乱。而且你不得不像一个程序员那样思考,并开始思考像COMMAND.COM/CMD.EXE,它几乎只能看到一行,而不是整个批处理文件作为原子单位。 事实上,这里有一个例子可以帮助你真正掌握它的工作方式。
创建一个文件夹,C:\ testing,并把下面的批处理文件叫做“oops.bat”:
@ECHO OFF ECHO Look mom, no brain! PAUSE ECHO Bye mom!
现在打开一个控制台窗口并运行它,但让它坐在那里暂停:
C:\testing>oops.bat Look mom, no brain! Press any key to continue . . .
在暂停时,在文本编辑器中打开oops.bat并将其更改为:
@ECHO OFF ECHO Look mom, no brain!? ECHO Oops! PAUSE ECHO Bye mom!
保存它,然后切换回您的控制台窗口并按任意键继续运行批处理文件:
'ops!' is not recognized as an internal or external command, operable program or batch file. Press any key to continue . . . Bye mom! c:\testing>
哇…看到那里的错误? 发生这种情况的原因是我们编辑了批处理文件,而它仍然由CMD.EXE运行,但是我们的编辑改变了批处理文件CMD.COM认为它的位置。 在内部,CMD.EXE维护一个文件指针,指示要处理的下一个字符的开始,在这种情况下,这个字符就是在PAUSE(和CRLF)行之后的字节。 但是当我们编辑它时,它改变了批处理文件中下一个命令的位置,但是CMD.EXE的指针仍然在同一个地方。 在这种情况下,它指向“ECHO Oops!”中间的字节位置。 所以它试图处理“ops!” 作为暂停后的命令。
我希望这可以清楚地表明,COMMAND.COM/CMD.EXE将总是将您的批处理文件视为字节流,而不是像脚本语言或编译器那样将其视为逻辑块,子例程等。 这就是批处理文件库如此有限的原因。 这使得将库导入到当前正在运行的批处理文件是不可能的。
呵呵,而且我还有另外一个想法..在现代Windows的CMD.EXE中,你总是可以创建一个批处理文件,它可以立即创建一个临时批处理文件,然后调用它:
@ECHO OFF SET TEMPBAT=%TEMP%\TMP%RANDOM:~0,1%%RANDOM:~0,1%%RANDOM:~0,1%%RANDOM:~0,1%.BAT ECHO @ECHO OFF > %TEMPBAT% ECHO ECHO Hi Mom! I'm %TEMPBAT%! >> %TEMPBAT% ECHO Hello, world, I'm %~dpnx0! CALL %TEMPBAT% DEL %TEMPBAT%
这有效地在临时目录中创建了一个名为TMP ####。BAT的临时批处理文件(其中#用随机数字替换;%RANDOM:〜0,1%表示将返回的数字的第一个数字%RANDOM% – 我们只需要一位数字,而不是RANDOM返回的全部数字),然后是ECHO的“Hello,World”,后面是它自己的全名(%〜dpnx0部分),CALLs临时批处理文件,这反过来是ECHO的“嗨,妈妈!” 然后是自己的[随机]名称,然后返回到原始的批处理文件,以便它可以执行任何需要清理的操作,例如在这种情况下删除临时批处理文件。
无论如何,正如你可以看到这篇文章的长度,这个话题真的不是一个简单的。 网上有几十个或更多的网页,有大量的批处理文件提示,技巧等,其中许多网页深入介绍如何使用它们,创建批处理文件库,注意什么,如何通过参考值与值的参数,如何管理何时何地变量扩展等等。
快速搜索“BATCH FILE PROGRAMMING”以搜索“BATCH FILE PROGRAMMING”,您还可以查看Wiki和WikiBooks,SS64.com,robvanderwoude.com以及DMOZ的http://www.dmoz.org/Computers/Software / Operating_Systems / x86 / DOS /编程/语言/批/目录更多的资源。
祝你好运!