Batch file how to call another batch file at a specified label or call and immediately goto a certain label?

前端 未结 5 1393
不知归路
不知归路 2021-01-02 00:57

I am trying to figure out how file1.bat can call file2.bat at a specified label.

I figured I can do it like this:

File1.bat

:config
         


        
相关标签:
5条回答
  • 2021-01-02 01:46

    You can use a strange trick!
    You can goto a label in a secondary batch without calling it in the secondary batch!

    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
    

    OUTPUT

    This is second.bat at LABEL
    returned
    

    There seems to be no cause why the label is called, nor why the control should return to the first.bat, as second batch was called without a CALL.
    The cause for the first point seems to be the internal code of the goto command.
    The second point can be explained, as there is one call prior to the dummy label in the first batch file.
    The exit /b in second.bat returns directly to the call (line 3) of first.bat not to the invocation of second.bat at line 7

    EDIT: How to disable the odd behaviour

    if you append a command at second.bat it will no longer implicit jump to the label in second.bat.

    second.bat & rem will change the output to OUTPUT

    Main of second.bat
    returned
    
    0 讨论(0)
  • 2021-01-02 01:54

    I just created a script that might prove useful to you. I think it's strength is that you don't have to modify your called script in any way. I'm sure with a bit of time you can perfect it and make it fit your purpose. I also can figure that it isn't fast enough for production use when the called script is large. This method consists on placing my script to the folder of your own scripts, and using it like so:

    EDIT

    Now this script works from any directory and can be input with either full or relative paths to the called script, with or without file extension (C:\scripts\script, C:\scripts\script.bat, .\scripts\script.cmd, etc)

    Usage:

    Given a sample script script.bat:

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

    The following input:

    CALLAT script :LABEL_2
    

    Should output:

    This is label number two!
    

    Source code of 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
    
    0 讨论(0)
  • 2021-01-02 02:01

    You could pass the label you want to go to as a parameter

    Example scripts

    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
    
    0 讨论(0)
  • 2021-01-02 02:03

    There are several methods or tricks that allows to do this. In this solution the trick consists in change the context of the running program to the second Batch file. After this change, all labels in the second Batch file becomes local labels of the running program, so they may be directly called via the usual call :label command that may also include arguments in an entirely standard way. The context of the running program must be recovered before the program terminates.

    The way to change the context consist is just rename the second Batch file with the same name of the first one (and rename the first one to any other name) so when the Batch file processor looks for a label, it really search the contents of the second file! The files must be renamed back to their original names before the program terminate. The key point that makes this method work is that both sets of rename commands (and all code between them) must be enclosed in parentheses; in this way, the code is first parsed and then executed from memory, so the rename of the files on disk don't affect it. Of course, this point means that the access to the values of all variables in the code must be done via delayed !expansion! (or using the call %%variable%% trick).

    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
    

    OUTPUT

    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...
    

    You should note that this method allows to develop the subroutines in the first file in the usual way (with no context change) and then just move the completed subroutines to the second/library file, because the way to call them is exactly the same in both cases. This method does not require any change in the second/library file nor in the way the subroutines are called; it just needs to insert the two small blocks of rename commands in the first file.


    EDIT: Restoring files when a run-time error happen

    As indicated in a comment, if a run-time error happen after the files were renamed, the files are not renamed back to their original names; however, it is very easy to detect this situation by the presence of the second.bat file and restore the files if such a file does not exists. This test may be done in a supervisor section that run the original code in a separate cmd.exe context, so this section will always complete correctly even if the original code was cancelled by an error.

    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
    

    OUTPUT

    I am label1 subroutine at second.bat
    Returned from :label1
    No se esperaba X en este momento.
    Error detected!  Restoring files
    
    0 讨论(0)
  • 2021-01-02 02:04

    As I said in the comment that I attached to my answer, I awarded the bounty, because @NicoBerrogorry took the time to devise an excellent solution, and provide a complete write-up.

    Nevertheless, there was one syntax error that he overlooked, probably because he doesn't use CMD files, while I have almost entirely abandoned BAT files. The following CALLAT script incorporates one correction and four enhancements.

    1. It handles fully specified commands, that is, commands that specify the file name with its extension.
    2. It corrects the oversight in the original that caused it to ignore CMD files.
    3. When the target file simply cannot be found, it displays an error message.
    4. The for loop that appends the file one line at a time is replaced with a decidedly old-school COPY command that appends the target script file to the two-line preamble that makes the whole thing work. In case you have forgotten or didn't know about it, the > NUL: at the end of the copy command discards the "1 file copied" message.
    5. While the original fulfilled its mission, it didn't clean up after itself; this version does so, by removing the temporary script file and the temporary directory in which it is created.

    @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\"

    Finally, this version of CALLAT eliminates a redundant ECHO OFF and the concluding ENDLOCAL, which I discovered long ago is unnecessary, because exiting a script implies ENDLOCAL.

    Since virtually all of my production scripts inhabit a single directory that is accessible via the PATH directory list, the requirement that both scripts inhabit the same directory isn't a significant hindrance. Moreover, with a little more work, it could almost certainly be relaxed. I'll leave that as an exercise for interested readers, but I'll give you one clue; it might involve calling an internal subroutine that can parse the name of the input file by leveraging the technique described [in Command Line Parameters].1

    The script shown above is about to find its way into production.

    0 讨论(0)
提交回复
热议问题