批处理文件如何在指定的标签上调用另一个批处理文件或调用并立即转到某个标签?

时间:2012-07-20 09:07:23

标签: label call batch-file

我试图找出file1.bat如何在指定的标签上调用file2.bat。

我想我可以这样做:

File1.bat

:config
@echo off
:setvars
set labelmarker=labelmarker
call file2.bat
pause > nul
:EOF

File2.bat

if %labelmarker%==labelmarker goto label4
:label1
echo it won't work...
goto EOF
:label2
echo it must work!
goto EOF
:label3
echo it didn't work...
goto EOF
:label4
echo it works!
goto EOF
:EOF

这很有效。但是我想从file1.bat调用bat和Label。 是否可以使用控制字符或ascii代码或任何东西? 就像我试过的那样

call file2.bat | goto label4 - doesn't work
call file2.bat > goto label4 - doesn't work
call file2.bat @label4 - doesn't work

非常感谢任何帮助。

即使涉及将特定标签和内容提取到新文件也没问题。

5 个答案:

答案 0 :(得分:9)

您可以将要转到的标签作为参数传递

示例脚本

First.bat

@echo off
set label=GOHERE
call Second.bat %label%
pause >nul

Second.bat

@echo off
goto %1
echo This line should be skipped
:GOHERE
echo Jumped here

答案 1 :(得分:7)

您可以使用奇怪的技巧
您可以转发二级批次中的标签,而无需在二级批次中调用它!

First.bat

@echo off
call :label
echo returned
exit /b

:label
second.bat
exit /b

Second.bat

@echo off
echo Main of second.bat
exit /b

:label
echo This is second.bat at LABEL
exit /b

输出

This is second.bat at LABEL
returned

似乎没有理由调用标签的原因,也没有为什么控件应该返回到 first.bat ,因为第二批称为而没有 > CALL 的。
第一点的原因似乎是goto命令的内部代码 第二点可以解释,因为在第一个批处理文件中的 dummy 标签之前有一个调用。
second.bat中的exit /b直接返回first.bat的调用(第3行)而不是第7行的second.bat调用

编辑:如何禁用奇怪的行为

如果您在second.bat附加命令,它将不再隐式跳转到 second.bat 中的标签。

second.bat & rem会将输出更改为 输出

Main of second.bat
returned

答案 2 :(得分:3)

有几种方法或技巧可以做到这一点。在此解决方案中,技巧包括将正在运行的程序的上下文更改为第二个Batch文件。在此更改之后,第二个批处理文件中的所有标签都成为正在运行的程序的本地标签,因此可以通过通常的call :label命令直接调用它们,该命令也可能包含完全标准的参数办法。必须在程序终止之前恢复正在运行的程序的上下文。

更改上下文组合的方法只是重命名第二个批处理文件,其名称与第一个相同(并将第一个重命名为任何其他名称),因此当批处理文件处理器看起来时对于标签,它真的搜索第二个文件的内容!在程序终止之前,必须将文件重命名为其原始名称。使该方法有效的关键点是两组重命名命令(以及它们之间的所有代码)必须括在括号中;通过这种方式,代码首先被解析,然后从内存执行,因此磁盘上文件的重命名不会影响它。当然,这一点意味着访问代码中所有变量的值必须通过延迟!扩展来完成! (或使用call %%variable%%技巧)。

First.bat

@echo off

rem Change the running context to the second/library batch file:
(
ren first.bat temporary.bat
ren second.bat first.bat

rem From this point on, you may call any label in second.bat like if it was a local label

call :label2
echo Returned from :label2
call :label1
echo Returned from :label1

rem Recover the running context to the original batch file
ren first.bat second.bat
ren temporary.bat first.bat
)

echo This is first.bat ending...
pause > nul

Second.bat

@echo off

:label1
echo I am label1 subroutine at second.bat
goto :EOF

:label2
echo This is the second subroutine at second.bat
goto :EOF

输出

This is the second subroutine at second.bat
Returned from :label2
I am label1 subroutine at second.bat
Returned from :label1
This is first.bat ending...

你应该注意到这个方法允许以通常的方式在第一个文件中开发子程序(没有上下文改变)然后只需将完成的子程序移动到第二个/库文件,因为在两种情况下,调用它们的方式都是完全相同的。此方法不需要对第二个/库文件进行任何更改,也不需要调用子例程的方式;它只需要在第一个文件中插入两个小块重命名命令。

编辑在发生运行时错误时还原文件

如评论中所示,如果在重命名文件后发生运行时错误,则文件不会重命名为其原始名称;但是,通过存在second.bat文件来检测这种情况非常容易,并且如果这样的文件不存在则还原文件。此测试可以在主管部分中完成,该部分在单独的cmd.exe上下文中运行原始代码,因此即使原始代码被错误取消,此部分也将始终正确完成。

First.bat

@echo off

if "%~1" equ "Original" goto Original

rem This supervisor section run the original code in a separate cmd.exe context
rem and restore the files if an error happened in the original code
(
cmd /C "%~F0" Original
if not exist second.bat (
   echo Error detected!  Restoring files
   ren first.bat second.bat & ren temporary.bat first.bat
)
goto :EOF
)


:Original

( ren first.bat temporary.bat & ren second.bat first.bat

call :label1
echo Returned from :label1
call :label2
echo Returned from :label2

ren first.bat second.bat & ren temporary.bat first.bat )

echo This is first.bat ending...

Second.bat

@echo off

:label1
echo I am label1 subroutine at second.bat
exit /B

:label2
set "var="
if %var% equ X echo This line cause a run-time error!
exit /B

输出

I am label1 subroutine at second.bat
Returned from :label1
No se esperaba X en este momento.
Error detected!  Restoring files

答案 3 :(得分:1)

我刚刚创建了一个可能对您有用的脚本。我认为它的优势在于你不必以任何方式修改你的被调用脚本。我确信有一点时间你可以完善它并使它符合你的目的。我还可以认为,当被调用的脚本很大时,它的生产速度不够快。此方法包括将我的脚本放在您自己脚本的文件夹中,并按如下方式使用它:

修改

现在,此脚本可以在任何目录中运行,并且可以使用完整或相对路径输入被调用脚本,带或不带文件扩展名(C:\ scripts \ script,C:\ scripts \ script.bat,。\ scripts \ script.cmd等)

用法:

给出示例脚本 script.bat

:LABEL_1
ECHO This is label number one!
GOTO :EOF

:LABEL_2
ECHO This is label number two!
GOTO :EOF

以下输入:

CALLAT script :LABEL_2

应输出:

This is label number two!

CALLAT.bat 的源代码:

@ECHO OFF
SETLOCAL EnableDelayedExpansion
@ECHO OFF

rem Create a folder in the temporary files directory
MKDIR "%TEMP%\CALLAT\"> NUL

rem Chose a name for a temporal script
SET TEMP_SCRIPT="%TEMP%\CALLAT\temp-script.bat"

rem Get the full path to the called script
FOR /F "delims=" %%F IN ('DIR /B /S "%1*" 2^>NUL') DO SET ORIGINAL_SCRIPT=%%F

rem Add this lines to go directly to the desired label in the temp script
ECHO @ECHO OFF> %TEMP_SCRIPT%
ECHO GOTO %2>> %TEMP_SCRIPT%

rem Concatenate the called script to the end of the temp script
COPY /B %TEMP_SCRIPT%+%ORIGINAL_SCRIPT% %TEMP_SCRIPT%>NUL

rem Call the modified script
CALL %TEMP_SCRIPT%

rem Clean up
RD "%TEMP%\CALLAT\" /S /Q>NUL

ENDLOCAL

答案 4 :(得分:0)

正如我在评论中所说,我附上了我的答案,我获得了赏金,因为@NicoBerrogorry花时间设计了一个出色的解决方案,并提供完整的报告。

然而,他忽略了一个语法错误,可能是因为他没有使用 CMD 文件,而我几乎完全放弃了 BAT 文件。以下CALLAT脚本包含一个更正和四个增强功能。

  1. 它处理完全指定的命令,即指定文件名 扩展名的命令。
  2. 它纠正了导致其忽略 CMD 文件的原始疏忽。
  3. 如果找不到目标文件,则会显示错误消息。
  4. 将一行附加一行的 for 循环替换为明确的老式 COPY 命令,该命令将目标脚本文件追加到两行使整个事情发挥作用的序言。如果您忘记或不知道,> NUL:在复制命令的末尾丢弃复制的" 1文件"消息。
  5. 虽然原来完成了它的使命,但它并没有自我清理;此版本通过删除临时脚本文件和创建它的临时目录来实现。
  6. 
    
    @ECHO OFF
    SETLOCAL EnableDelayedExpansion
    
    rem Create a folder in the temporary files directory.
    IF NOT EXIST "%TEMP%\CALLAT\" MKDIR "%TEMP%\CALLAT\"
    
    rem Chose a name for the modified script.
    SET MODIFIED_SCRIPT="%TEMP%\CALLAT\modified_%1.bat"
    
    rem Convert the called script name to the corresponding .bat
    rem or .cmd full path to the called script.
    
    rem Then load the script. - 2017/05/08 - DAG - The second if exist test duplicated the first. Instead, it must evaluate for the .CMD extension.
    
    SET "COMMAND_FILE=%~dp0%1"
    IF EXIST "%COMMAND_FILE%.bat" (
        SET "COMMAND_FILE=%COMMAND_FILE%.bat"
    ) ELSE IF EXIST "%COMMAND_FILE%.cmd" (
        SET "COMMAND_FILE=%COMMAND_FILE%.cmd"
    ) else if exist "%COMMAND_FILE%" (
    	echo INFO: Using "%COMMAND_FILE%"
    ) else (
    	echo ERROR: Cannot find script file %COMMAND_FILE%.bat
    	echo                             or %COMMAND_FILE%.cmd
    )
    
    rem Add some lines to go directly to the desired label.
    ECHO @ECHO OFF> %MODIFIED_SCRIPT%
    ECHO GOTO %2>> %MODIFIED_SCRIPT%
    
    rem Append the called script.
    copy %MODIFIED_SCRIPT%+%COMMAND_FILE% %MODIFIED_SCRIPT% > NUL:
    
    rem Call the modified script.
    CALL %MODIFIED_SCRIPT%
    
    rem Pack out your trash. When a script ends, you get an ENDLOCAL for free.
    del %MODIFIED_SCRIPT%
    rd "%TEMP%\CALLAT\"
    
    
    

    最后,这个版本的 CALLAT 消除了多余的 ECHO OFF ,结论 ENDLOCAL ,我很久以前就发现了这一点是不必要的,因为退出脚本意味着 ENDLOCAL

    由于几乎所有的生产脚本都存在于可通过PATH目录列表访问的单个目录中,因此要求两个脚本都位于同一目录中并不是一个重要的障碍。此外,通过更多的工作,几乎可以放松。我会把它留作感兴趣读者的练习,但我会给你一个线索;它可能涉及调用一个内部子程序,它可以通过[在命令行参数]中描述的技术来解析输入文件的名称。1

    上面显示的脚本即将投入生产。