我的问题与this问题有关。我有几堆需要从批处理文件执行的操作,我想将它们建模为函数并从主序列调用。从上面的问题来看,很明显我可以使用调用语法
来做到这一点call:myDosFunc
我的问题是,我可以将所有这些功能放在一个单独的批处理文件(functions.bat)中,并以某种方式'包含'在主批处理文件中并调用它们吗?另一种选择是利用调用syntaxt从main.bat调用functions.bat的可能性,但我不确定是否可以使用特定函数调用它而不是执行整个批处理文件。
简而言之,我正在寻找类似于C编程世界的东西,我的函数驻留在DLL中,主程序只包含高级逻辑,并从DLL中调用函数。
答案 0 :(得分:8)
我认为批处理文件开头的路由功能并不那么难看。 您可以在“ 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“抛出”的错误级别(退出/ b移交当前的错误级别)。 使用第二个调用而不是goto,确保“%1”==“param1”而不是func2。 如果标签不存在,调用将不会终止批处理文件,它只是将errorlevel设置为1并将错误消息放入2(错误输出),这可以重定向到nul。
说明:%*包含所有参数,因此在示例中,第一行转换为:
call:func2 params1 param2 ... paramN
答案 1 :(得分:6)
以下是一个如何完成的简单示例。
调用函数脚本时,函数名称为第一个参数,函数参数为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,这是一个用于处理批处理文件中的字符和字符串的例程库。显示库开发的线程可用here
几年前我写过CharLib.bat。如果我今天写它,我可能会使用GOTO而不是CALL。
引入CALL的问题在于,在将字符串文字作为参数传递时会产生问题。额外的CALL意味着包含%
的字符串文字必须使百分比加倍一倍的额外时间。它还意味着需要将额外时间转义为&
和|
等不带引号的毒药字符。调用者可以解决这两个问题。但真正的问题是,每个CALL都会引用引用的插入符号:"^"
变为"^^"
。没有一种解决插入符号倍增问题的好方法。
额外CALL的问题不会影响CharLib.bat,因为字符串值是通过引用(变量名称)传递的,而不是字符串文字。
使用带有SHIFT / 1的GOTO的唯一不利方面是你不能使用%0
来获取当前正在执行的例程的名称。我可以在没有/ 1的情况下使用SHIFT,但是你不能在例程中使用%~f0
来获取执行批处理文件的完整路径。
答案 2 :(得分:3)
您可以使用此格式 - 并按以下方式启动:
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
答案 3 :(得分:3)
你可以使用一个有趣的技巧来避免其他方法在尝试使库函数可用于主程序时遇到的大多数问题而且速度要快得多。使用这个技巧的唯一要求是:
技巧包括“切换正在运行的批处理文件的上下文”,其方式是库文件成为正在运行的批处理文件;这样,库文件中的所有函数都可用于主代码块而无需额外处理。当然,在代码块结束之前,必须将正在运行的批处理文件的“上下文”切换回主文件。
“切换上下文”的方法是使用与正在运行的主文件相同的名称重命名库文件(并将主文件重命名为另一个名称)。例如:
(
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
答案 4 :(得分:1)
我不确定原始问题的上下文,但这可能是使用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)的作用就像每次运行批处理文件时一个接一个地手动键入这些行。它确实没有像lexica那样坚实的概念,并且像普通的编程/脚本语言一样具有范围 - 也就是说,它不会像调用堆栈那样维护更多的额外元数据,等等。它所做的一点点维护更多的是它是一个事后的想法,而不是从一开始就内置到批处理文件中。
然而,一旦你改变了想法,你就可以使用各种技巧和技术来克服这种限制,以模拟更强大的脚本/编程语言;但你仍然需要记住批处理文件仍然有限,无论如何。
无论如何,使用批处理文件库的一种技术是创建一个批处理文件,其中第一个参数用于指示调用哪个函数:
CALL BATLIB.BAT FunctionName Parameter1 Parameter2 ...
当编写库时考虑到这一点,这很有效,因此它会知道跳过第一个参数,等等。
在Windows中使用更现代版的CMD.EXE'系统允许使用":标签"在CALL语法中,如果你想限制参数范围(允许你使用%*作为"所有参数",例如),这可能很有用,如下所示:
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 _ 取代 整个%PATH% _ 之前它甚至处理CALL命令。如果你的路径中有空格可以解决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!
现在打开一个控制台窗口并运行它,但让它坐在PAUSE:
C:\testing>oops.bat
Look mom, no brain!
Press any key to continue . . .
当它坐在PAUSE中时,在文本编辑器中打开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中间的字节位置!"线,所以它试图处理"操作!"作为PAUSE之后的命令。
我希望这清楚地表明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部分),调用临时批处理文件,而这又是ECHO"嗨妈妈!"然后是它自己的[随机]名称,然后返回到原始批处理文件,以便它可以执行任何需要的清理,例如在这种情况下删除临时批处理文件。
无论如何,正如你可以看到这篇文章的篇幅,这个话题真的不是一个简单的话题。网上有数十个或更多的网页,有大量的批处理文件提示,技巧等等,其中许多都深入探讨了如何使用它们,创建批处理文件库,需要注意什么,如何通过引用和值传递参数,如何管理变量扩展的时间和位置,等等。
快速谷歌搜索"批量文件编程"找到它们中的许多,你还可以查看Wiki和WikiBooks,SS64.com,robvanderwoude.com甚至DMOZ的http://www.dmoz.org/Computers/Software/Operating_Systems/x86/DOS/Programming/Languages/Batch/目录以及更多资源。
祝你好运!答案 5 :(得分:1)
这是我编写的cmd批处理脚本,导入 文件或文件夹中的文件(递归)到主脚本中< / strong>:
@echo off
REM IMPORT - a .cmd utility for importing subroutines into the main script
REM Author: Ioan Marin
REM !!! IN ORDER TO FUNCTION CORRECTLY: !!!
REM !!! IMPORT MUST BE CALLED INSIDE A DISABLED DELAYED EXPANSION BLOCK/ENVIRONMENT (DEFAULT) !!!
rem \\// Define import file mask here:
rem If mask is not defined outside "import.cmd":
if not defined mask (
set "mask=*.cmd; *.bat"
)
rem //\\ Define import file mask here:
rem Detect if script was started from command line:
call :DetectCommandLine _not_started_from_command_line
if "%~1" == "/install" (
set "import_path=%~dp0"
call :EscapePathString import_path import_path_escaped
)
if not "%~1" == "" (
if /i not "%~1" == "end" (
if "%~1" == "/?" (
call :DisplayHelp
) else (
if "%~1" == "/install" (
echo Installing
set "_first_time="
rem This should get into the Autorun registry key: path %path%;"...\import.cmd"
rem If you want, other commands can be added to the left or to the right of this command, unified as a block with a "&" command operator
REG ADD "HKCU\Software\Microsoft\Command Processor" /v AutoRun /t REG_SZ /d "path %%path%%;"""%import_path_escaped%""||(
echo ERROR: Cannot install import: cannot write to the registry^!
echo You can try to manually add the "import.cmd" path in the "PATH" variable or use pushd ^(see help^).
if not "%_not_started_from_command_line%" == "0" (
call :PressAnyKey Press any key to exit...
echo.
)
exit /b 1
)
echo.
echo Done. The install directory was set to:
echo "%import_path%"
echo and was added to the PATH environment variable. You can add other desired programs into this directory.
echo.
echo Please note that the console needs to be closed and reopened in order for the changes to take effect^!
echo.
) else (
if not defined _first_time (
set _first_time=defined
set /a count=0
if "%_DISPLAY_WARNING%" == "true" (
echo.
echo WARNING: CMD_LIBRARY was reset to "", because previously it gave an error^!
echo.
)
echo Loading list to import...
)
REM build import files list
set /a count+=1
call set "_import_list_%%count%%=%%~1"
)
)
)
) else (
call :DisplayHelp
)
if /i "%~1" == "end" (
set "_first_time="
echo Done.
echo Analyzing...
rem set "_main_program=%~dpnx2"
if not exist "%~dpnx2" (
echo ERROR: Second parameter, after "import end", must be a valid file path - see help^!>>&2
rem Clean up
call :CleanUp
if not "%_not_started_from_command_line%" == "0" (
call :PressAnyKey Press any key to exit...
echo.
)
exit /b 1
)
)
if /i "%~1" == "end" (
set "_main_batch_script=%~dpnx2"
rem \\// Define output filename here:
rem set "_output_filename=tmp0001_%~n2.cmd"
set "_output_filename=tmp0001.cmd"
rem //\\ Define output filename here:
)
if /i "%~1" == "end" (
rem Check all paths not to be UNC:
setlocal EnableDelayedExpansion
set "_error=false"
call :TestIfPathIsUNC _main_batch_script _result
if "!_result!" == "true" (
set "_error=true"
echo.
echo ERROR: UNC paths are not allowed: Second parameter, after "import end", must not be a UNC path^^^! Currently it is: "!_main_batch_script!">>&2
)
set "_CMD_LIBRARY_error=false"
call :TestIfPathIsUNC CMD_LIBRARY _result
if "!_result!" == "true" (
set "_error=true"
set "_CMD_LIBRARY_error=true"
echo.
echo ERROR: UNC paths are not allowed: CMD_LIBRARY variable must not contain a UNC path^^^! Currently, it is set to: "!CMD_LIBRARY!".>>&2
)
for /l %%i in (1,1,!count!) do (
call :TestIfPathIsUNC _import_list_%%i _result
if "!_result!" == "true" (
set "_error=true"
echo.
echo ERROR: UNC paths are not allowed: The import path: "!_import_list_%%i!" is a UNC path^^^!>>&2
)
)
if "!_error!" == "true" (
echo.
echo Errors were ecountered^^^!
if "!_CMD_LIBRARY_error!" == "true" (
endlocal
set "_CMD_LIBRARY_error=true"
) else (
endlocal
set "_CMD_LIBRARY_error=false"
)
rem Clean up
call :CleanUp
if not "%_not_started_from_command_line%" == "0" (
call :PressAnyKey Press any key to exit...
echo.
)
exit /b 1
) else (
endlocal
set "_CMD_LIBRARY_error=false"
)
)
if /i "%~1" == "end" (
rem Check all paths not to contain "*" and "?" wildcards:
set "_asterisk=*"
set "_qm=?"
setlocal EnableDelayedExpansion
set "_error=false"
call :TestIfStringContains _main_batch_script _asterisk _result1
call :TestIfStringContains _main_batch_script _qm _result2
if "!_result1!" == "true" (
set "_error=true"
)
if "!_result2!" == "true" (
set "_error=true"
)
if "!_error!" == "true" (
echo.
echo ERROR: The use of "*" or "?" wildcards is not supported by import: Second parameter, after "import end", must not contain "*" or "?" wildcards^^^! Currently it is: "!_main_batch_script!". Instead, you can set the mask with a set of name and extension masks sepparated by semicolon, like: set mask="*.cmd; *.bat">>&2
)
set "_CMD_LIBRARY_error=false"
call :TestIfStringContains CMD_LIBRARY _asterisk _result1
call :TestIfStringContains CMD_LIBRARY _qm _result2
if "!_result1!" == "true" (
set "_error=true"
)
if "!_result2!" == "true" (
set "_error=true"
)
if "!_error!" == "true" (
set "_error=true"
set "_CMD_LIBRARY_error=true"
echo.
echo ERROR: The use of "*" or "?" wildcards is not supported by import: CMD_LIBRARY variable must not contain "*" or "?" wildcards^^^! Currently, it is set to: "!CMD_LIBRARY!". Instead, you can set the mask with a set of name and extension masks sepparated by semicolon, like: set mask="*.cmd; *.bat">>&2
)
for /l %%i in (1,1,!count!) do (
call :TestIfStringContains _import_list_%%i _asterisk _result1
call :TestIfStringContains _import_list_%%i _qm _result2
if "!_result1!" == "true" (
set "_error=true"
)
if "!_result2!" == "true" (
set "_error=true"
)
if "!_error!" == "true" (
set "_error=true"
echo.
echo ERROR: The use of "*" or "?" wildcards is not supported by import: The import path: "!_import_list_%%i!" must not contain "*" or "?" wildcards^^^! Instead, you can set the mask with a set of name and extension masks sepparated by semicolon, like: set mask="*.cmd; *.bat">>&2
)
)
if "!_error!" == "true" (
echo.
echo Errors were ecountered^^^!
if "!_CMD_LIBRARY_error!" == "true" (
endlocal
set "_CMD_LIBRARY_error=true"
) else (
endlocal
set "_CMD_LIBRARY_error=false"
)
rem Clean up
call :CleanUp
if not "%_not_started_from_command_line%" == "0" (
call :PressAnyKey Press any key to exit...
echo.
)
exit /b 1
) else (
endlocal
set "_CMD_LIBRARY_error=false"
)
)
if /i "%~1" == "end" (
pushd "%~dp2"
call set "_output_dir=%%CD%%"
popd
)
if /i "%~1" == "end" (
if not defined CMD_LIBRARY (
set CMD_LIBRARY_CASE=IMPORT.CMD
set "BASE=%~dpnx0\.."
pushd "%~dpnx0"\..\
REM \\// Define CMD LIBRARY here ("." is relative to "import.cmd" parent directory):
REM if CMD_LIBRARY is not defined outside import.cmd, "." (used here) is related to import.cmd parent directory:
set "CMD_LIBRARY=."
REM //\\ Define CMD LIBRARY here ("." is relative to "import.cmd" parent directory):
) else (
set CMD_LIBRARY_CASE=MAIN.CMD
set "BASE=%~dpnx2\.."
REM if CMD_LIBRARY is defined outside the "import.cmd" script, "." (used in CMD_LIBRARY) is related to "main program" parent directory
pushd "%~dpnx2"\..
)
)
if /i "%~1" == "end" (
call :DeQuoteOnce CMD_LIBRARY CMD_LIBRARY
call set "CMD_LIBRARY_ORIGINAL=%%CMD_LIBRARY%%"
call :TestIfPathIsUNC CMD_LIBRARY_ORIGINAL _result
setlocal EnableDelayedExpansion
if "!_result!" == "true" (
set "_error=true"
echo.
echo ERROR: UNC paths are not allowed: CMD_LIBRARY variable must not contain a UNC path^^^! Currently, it is set to: "!CMD_LIBRARY_ORIGINAL!".>>&2
echo.
echo Errors were ecountered^^^!
endlocal
set "_CMD_LIBRARY_error=true"
rem Clean up
call :CleanUp
if not "%_not_started_from_command_line%" == "0" (
call :PressAnyKey Press any key to exit...
echo.
)
exit /b 1
) else (
endlocal
set "_CMD_LIBRARY_error=false"
)
call pushd "%%CMD_LIBRARY%%" >nul 2>nul&&(
call set "CMD_LIBRARY=%%CD%%"
)||(
call echo ERROR: Could not access directory CMD_LIBRARY=^"%%CMD_LIBRARY%%^"^!>>&2
call :CleanUp
if not "%_not_started_from_command_line%" == "0" (
call :PressAnyKey Press any key to exit...
echo.
)
popd
exit /b 1
)
)
if /i "%~1" == "end" (
setlocal EnableDelayedExpansion
set _error=false
pushd "!BASE!"
echo.
if "!CMD_LIBRARY_CASE!" == "IMPORT.CMD" (
echo CMD_LIBRARY was defined as: "!CMD_LIBRARY_ORIGINAL!" in the file "import.cmd" and was expanded to: "!CMD_LIBRARY!"
) else (
if "!CMD_LIBRARY_CASE!" == "MAIN.CMD" (
echo CMD_LIBRARY was defined as: "!CMD_LIBRARY_ORIGINAL!" outside "import.cmd" file "%~nx2" and was expanded to: "!CMD_LIBRARY!"
)
)
for /l %%i in (1,1,!count!) do (
if not exist "!_import_list_%%i!" (
if not exist "!CMD_LIBRARY!\!_import_list_%%i!" (
rem if first time:
if not "!_error!" == "true" (
echo.
echo Directory of "!CMD_LIBRARY!":
)
echo.
echo ERROR: element "!_import_list_%%i!" does not exist or is not accessible as a standalone file/dir or as a file/dir in the directory contained by "CMD_LIBRARY" variable^^^!>>&2
set _error=true
)
)
)
popd
if "!_error!" == "true" (
endlocal
rem Clean up
call :CleanUp
if not "%_not_started_from_command_line%" == "0" (
call :PressAnyKey Press any key to exit...
echo.
)
exit /b 1
) else (
endlocal
)
echo OK
echo.
)
set "_error=false"
if /i "%~1" == "end" (
echo Output file is: "%_output_dir%\%_output_filename%"
echo.
echo Importing...
echo.
(
type nul>"%_output_dir%\%_output_filename%"
) 2>nul||(
echo ERROR: Could not write to file: "%_output_dir%\%_output_filename%"^!>>&2
rem Clean up
call :CleanUp
if not "%_not_started_from_command_line%" == "0" (
call :PressAnyKey Press any key to exit...
echo.
)
exit /b 1
)
echo Importing main script "%_main_batch_script%"
(
echo @set _import=defined
echo @REM Timestamp %date% %time%
echo.
)>>"%_output_dir%\%_output_filename%"
(
(
type "%_main_batch_script%"
)>>"%_output_dir%\%_output_filename%"
) 2>nul||(echo ERROR: Could not read file^!&set "_error=true">>&2)
(
echo.
echo.
)>>"%_output_dir%\%_output_filename%"
echo.
echo Directory of "%CMD_LIBRARY%":
if not "%CMD_LIBRARY_CASE%" == "MAIN.CMD" (
pushd "%BASE%"
)
if not defined mask (
rem If mask is not defined, import all file types:
set "mask=*"
)
for /l %%i in (1,1,%count%) do (
call set "_import_list_i=%%_import_list_%%i%%"
call :ProcedureImportCurrentFile
)
if not "%CMD_LIBRARY_CASE%" == "MAIN.CMD" (
popd
)
)
if "%~1" == "end" (
if "%_error%" == "true" (
echo.
echo Errors were ecountered^!
rem Clean up
call :CleanUp
if not "%_not_started_from_command_line%" == "0" (
call :PressAnyKey Press any key to exit...
echo.
)
exit /b 1
) else (
echo Done^!
)
call popd
popd
rem Clean up
call :CleanUp
rem Detect if script was started from command line:
call :DetectCommandLine _not_started_from_command_line
)
if "%~1" == "end" (
if "%_not_started_from_command_line%" == "0" (
set "_import="
) else (
echo.
echo Starting program...
echo.
rem Start "resulting" program:
"%_output_dir%\%_output_filename%"
)
)
goto :eof
REM \\\/// Next subroutines use jeb's syntax for working with delayed expansion: \\\///
:CleanUp
(
setlocal EnableDelayedExpansion
)
(
endlocal
if "%_CMD_LIBRARY_error%" == "true" (
set "CMD_LIBRARY="
set "_DISPLAY_WARNING=true"
) else (
set "_DISPLAY_WARNING=false"
)
set "_first_time="
for /l %%i in (1,1,%count%) do (
set "_import_list_%%i="
)
rem optional:
set "count="
set "import_path="
rem set "_output_dir="
set "_error="
set "_main_batch_script="
rem set "_output_filename="
rem set "_import="
set "mask="
exit /b
)
:GetStrLen - by jeb - adaptation
(
setlocal EnableDelayedExpansion
set "s=!%~1!#"
set "len=0"
for %%P in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if "!s:~%%P,1!" NEQ "" (
set /a "len+=%%P"
set "s=!s:~%%P!"
)
)
)
(
endlocal
set "%~2=%len%"
exit /b
)
:EscapePathString
(
setlocal EnableDelayedExpansion
set "string=!%~1!"
call :GetStrLen string string_len
set /a string_len-=1
for /l %%i in (0,1,!string_len!) do (
rem escape "^", "(", ")", "!", "&"
if "!string:~%%i,1!" == "^" (
set "result=!result!^^^^"
) else (
if "!string:~%%i,1!" == "(" (
set "result=!result!^^^("
) else (
if "!string:~%%i,1!" == ")" (
set "result=!result!^^^)"
) else (
if "!string:~%%i,1!" == "^!" (
set "result=!result!^^^!"
) else (
if "!string:~%%i,1!" == "&" (
set "result=!result!^^^&"
) else (
if "!string:~%%i,1!" == "%%" (
set "result=!result!%%"
) else (
set "result=!result!!string:~%%i,1!"
)
)
)
)
)
)
)
)
(
endlocal
set "%~2=%result%"
exit /b
)
:PressAnyKey
set /p=%*<nul
pause>nul
goto :eof
:DeQuoteOnce
(
setlocal EnableDelayedExpansion
set "string=!%~1!"
if "!string!" == """" (
endlocal
set "%~2=%string%"
exit /b
)
rem In order to work with " we replace it with a special character like < > | that is not allowed in file paths:
set "string=!string:"=^<!"
if "!string:~0,1!" == "<" (
if "!string:~-1,1!" == "<" (
set "string=!string:~1,-1!"
)
)
rem restore " in string (replace < with "):
set "string=!string:<="!"
)
(
endlocal
set "%~2=%string%"
exit /b
)
:TestIfPathIsUNC
(
setlocal EnableDelayedExpansion
set "_current_path=!%~1!"
set "_is_unc_path=true"
if defined _current_path (
if "!_current_path:\\=!" == "!_current_path!" (
set "_is_unc_path=false"
)
) else (
set "_is_unc_path=false"
)
)
(
endlocal
set "%~2=%_is_unc_path%"
exit /b
)
:TestIfStringContains
(
setlocal EnableDelayedExpansion
echo "!%~1!"|find "!%~2!">nul 2>nul
set "_error_code=!ERRORLEVEL!"
)
(
endlocal
if "%_error_code%" == "0" (
set "%~3=true"
) else (
set "%~3=false"
)
exit /b
)
REM ///\\\ The subroutines above use jeb's syntax for working with delayed expansion: ///\\\
:DetectCommandLine
setlocal
rem Windows: XP, 7
for /f "tokens=*" %%c in ('echo "%CMDCMDLINE%"^|find "cmd /c """ /c') do (
set "_not_started_from_command_line=%%~c"
)
if "%_not_started_from_command_line%" == "0" (
rem Windows: 10
for /f "tokens=*" %%c in ('echo "%CMDCMDLINE%"^|find "cmd.exe /c """ /c') do (
set "_not_started_from_command_line=%%~c"
)
)
endlocal & (
set "%~1=%_not_started_from_command_line%"
)
goto :eof
:ProcedureImportCurrentFile
setlocal
set "cc="
if not exist "%_import_list_i%" (
set "_not_a_dir=false"
pushd "%CMD_LIBRARY%\%_import_list_i%\" 1>nul 2>&1||set "_not_a_dir=true"
call :GetStrLen CD _CD_len
)
if "%_not_a_dir%" == "false" (
setlocal EnableDelayedExpansion
if not "!CD:~-1,1!" == "\" (
endlocal
set /a _CD_len+=1
) else (
endlocal
)
popd
)
if not exist "%_import_list_i%" (
if "%_not_a_dir%" == "true" (
echo Importing file "%CMD_LIBRARY%\%_import_list_i%"
(
type "%CMD_LIBRARY%\%_import_list_i%">>"%_output_dir%\%_output_filename%"
) 2>nul||(
echo ERROR: Could not read file^!>>&2
set "_error=true"
)
(
if not "%%i" == "%count%" (
echo.
echo.
) else (
echo.
)
)>>"%_output_dir%\%_output_filename%"
) else (
echo Importing dir "%_import_list_i%"
rem
pushd "%CMD_LIBRARY%\%_import_list_i%\"
set /a cc=0
for /r %%f in (%mask%); do (
set "_current_file=%%~dpnxf"
call set "r=%%_current_file:~%_CD_len%%%"
call echo Importing subfile "%%_import_list_i%%\%%r%%"
(
(
call type "%%_current_file%%"
)>>"%_output_dir%\%_output_filename%"
) 2>nul||(
echo ERROR: Could not read file^!>>&2
set "_error=true"
)
(
echo.
echo.
)>>"%_output_dir%\%_output_filename%"
set /a cc+=1
)
popd
)
) else (
set "_not_a_dir=false"
pushd "%_import_list_i%\" 1>nul 2>&1||set "_not_a_dir=true"
call :GetStrLen CD _CD_len
)
if "%_not_a_dir%" == "false" (
setlocal EnableDelayedExpansion
if not "!CD:~-1,1!" == "\" (
endlocal
set /a _CD_len+=1
) else (
endlocal
)
popd
)
if exist "%_import_list_i%" (
if "%_not_a_dir%" == "true" (
echo Importing file "%_import_list_i%"
(
type "%_import_list_i%">>"%_output_dir%\%_output_filename%"
) 2>nul||(
echo ERROR: Could not read file^!>>&2
set "_error=true"
)
(
if not "%%i" == "%count%" (
echo.
echo.
) else (
echo.
)
)>>"%_output_dir%\%_output_filename%"
) else (
rem
echo Importing dir "%_import_list_i%"
pushd "%_import_list_i%\"
set /a cc=0
for /r %%f in (%mask%); do (
set "_current_file=%%~dpnxf"
call set "r=%%_current_file:~%_CD_len%%%"
call echo Importing subfile "%%_import_list_i%%\%%r%%"
(
(
call type "%%_current_file%%"
)>>"%_output_dir%\%_output_filename%"
) 2>nul||(
echo ERROR: Could not read file^!>>&2
set "_error=true"
)
(
echo.
echo.
)>>"%_output_dir%\%_output_filename%"
set /a cc+=1
)
popd
)
)
if "%cc%" == "0" (
echo No match^!
)
endlocal & (
set "_error=%_error%"
)
goto :eof
:DisplayHelp
echo IMPORT - a .cmd utility for importing subroutines into the main script
echo.
echo NOTES: 1. This utility assumes that command extensions are enabled (default) and that delayed expansion can be enabled;
echo ALSO IMPORT MUST BE CALLED INSIDE A DISABLED DELAYED EXPANSION BLOCK/ENVIRONMENT (DEFAULT);
echo These are necessary in order for it to function correctly.
echo 2. The use of UNC paths is not supported by import. As a workarround, you can mount a UNC path to a temporary drive using "pushd".
echo The use of "*" or "?" wildcards is not supported by import. Instead, you can set the mask with a set of name and extension masks sepparated by semicolon, like: set mask="*.cmd; *.bat"
echo When the "mask" variable is set, only the filenames having the extensions contained by it are matched at import.
echo.
echo Description:
echo import organizes your batch programs on common libraries of subroutines, that you can use in the future for other programs that you build; it also makes code editing and debugging easier.
echo.
echo Usage [1]:
echo import [flags]
echo.
echo [flags] can be:
echo /install - installs import into the registry, in the Command Processor AutoRun registry key ^(adds the current location of import into the PATH variable^).
echo /? - displays help ^(how to use import^)
echo.
echo Usage [2]:
echo What it does:
echo Concatenates ^(appends^) files content containing subroutines to the main program content using the following SYNTAX:
echo REM \\//Place this in the upper part of your script ^(main program)^ \\//:
echo.
echo @echo off
echo.
echo if not defined _import ^(
echo rem OPTIONAL ^(before the "import" calls^):
echo set "CMD_LIBRARY=^<library_directory_path^>"
echo.
echo import "[FILE_PATH1]filename1" / "DIR_PATH1"
echo ...
echo import "[FILE_PATHn]filenamen" / "DIR_PATHn"
echo import end "%%~0"
echo ^)
echo.
echo REM //\\Place this in the upper part of your script ^(main program)^ //\\:
echo.
echo "filename1" .. "filenamen" represent the filenames that contain the subroutines that the user wants to import in the current ^(main^) program. The paths of these files are relative to the directory contained in the CMD_LIBRARY variable.
echo.
echo "FILE_PATH1" .. "FILE_PATHn" represent the paths of these files.
echo.
echo "DIR_PATH1" .. "DIR_PATHn" represent directories paths in which to recursivelly search and import all the files of the type defined in the variable "mask"
echo.
echo CMD_LIBRARY is a variable that contains the directory path where your library of files ^(containing subroutines^) is found.
echo.
echo We denote the script that calls "import" as "the main script".
echo.
echo By default, if not modified in outside the import.cmd script, in the import.cmd script - CMD_LIBRARY is set to "." directory and is relative to the "import.cmd" parent directory.
echo If CMD_LIBRARY directory is modified outside the import.cmd script, CMD_LIBRARY is relative to the main script parent directory.
echo.
echo Note that only the last value of "CMD_LIBRARY" encountered before `import end "%%~0"` is taken into consideration.
echo.
echo import end "%%~0" - marks the ending of importing files and the start of building of the new batch file ^(named by default tmp0001.cmd, and located in the directory in which the main script resides^).
echo.
echo "%%~0" represents the full path of the main script.
echo.
echo Author: Ioan Marin
goto :eof
要使用它:
另存为import.cmd
使用/install
标志对其进行调用以进行安装(不需要管理员)
在您的主脚本的开头添加这样的标头,该标头调用存储在其他文件(将要导入的文件)中的子例程:
if not defined _import (
rem OPTIONAL (before the "import" calls):
set "CMD_LIBRARY=<library_directory_path>"
import "[FILE_PATH1]filename1" / "DIR_PATH1"
...
import "[FILE_PATHn]filenamen" / "DIR_PATHn"
import end "%~0"
)
要了解如何使用它,只需使用/?
标志对其进行调用。