如何在不同的 .bat 文件中调用动态标签

How to call a dynamic label in different .bat file

如果我想从 bar.bat 调用 foo.bat 中的 :foo 我会这样做:

::foo.bat
echo.wont be executed
exit /b 1
:foo
echo foo from foo.bat
exit /b 0

::bar.bat
call :foo
exit /b %errorlevel%
:foo
foo.bat
echo.will also not be executed

但如果我不知道标签名称但将其作为参数传递,我就会陷入困境

::bar.bat
:: calling a dynamic label is no problem
call :%~1
exit /b %errorlevel%
::don't know how to "catch-all" or set context of "current-label"
:%~1
foo.bat

foo.bat(中学):

@echo off
echo   this is foo.bat
REM check, if the label is defined in this script:
findstr /xi "%~1" %~f0 >nul 2>&1 || goto :error
goto %~1

:foo
echo     reached foo.bat, label :foo
exit /b 0

:error
echo       wrong or missing label: "%~1"
exit /b 1

bar.bat(小学)

@echo off
echo this is bar.bat
call foo.bat :foo
echo back to bar.bat - %errorlevel%
call foo.bat :foe
echo back to bar.bat - %errorlevel%
call foo.bat
echo back to bar.bat - %errorlevel%
exit /b

您可以使用批量解析器技巧。
您无需在 foo.bat 中执行任何操作即可运行

::foo.bat
echo.wont be executed
exit /b 1
:func1
echo foo from foo.bat
exit /b 0

:unknown
echo Hello from %0 Func :unknown
exit /b

你只需要在 bar.bat 中添加你想在 foo.bat

中调用的任何标签
@echo off
::bar.bat
call :unknown
echo Back from Foo
call :func1
echo Back from Foo
exit /b %errorlevel%

:unknown
:func1
foo.bat
echo NEVER COMES BACK HERE

诀窍是在 bar.bat 中调用一个标签然后开始 foo.bat 而没有 调用它(仅 foo.bat), foo.bat加载完毕,跳转到上次调用的标签。

正如 aschipfl 已经正确指出的那样,您不能拥有动态标签,但您可以动态调用当前标签,但您应该像 Stephan 那样在调用它之前检查它是否存在,但在第一批中。
所以这是 Stephans 和 jebs 批次的组合。

:: bar.bat
@echo off
REM check, if the label is defined in this script:
If "%~1" neq "" findstr /xi "%~1" %~f0 >nul 2>&1||goto :error&&Call :%~1
call :Func1
echo Back from Foo
call :func2
echo Back from Foo
exit /b %errorlevel%

:error
echo       wrong or missing label: "%~1"
exit /b 1

:func1
:func2
foo.bat
echo NEVER COMES BACK HERE

:: foo.bat 
@Goto :Eof

:func1
echo     reached foo.bat, label :func1
exit /b 0

:func2
echo     reached foo.bat, label :func2
exit /b 0

示例输出:

> bar
    reached foo.bat, label :func1
Back from Foo
    reached foo.bat, label :func2
Back from Foo

> bar fx
      wrong or missing label: "fx"

在解析环境变量或参数引用之前,解析器会逐字搜索标签,因此您不能在标签中使用此类。
但是,在此我想提供一种允许使用动态标签的方法,虽然我不明白这样做的目的是什么,所以这更像是一个学术性的答案...

cmd的批处理文件解析器不缓存批处理文件,它是逐行读取并执行的,或者正确地说,它是单独读取并执行每个命令line/block。因此,我们可以利用它并让批处理文件在执行期间自行修改,前提是修改的部分超出了当前执行的代码部分。以下脚本实现了这一点:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Check whether a label name has been delivered:
if "%~1"=="" (
    echo ERROR: No label name specified! 1>&2
    exit /B 1
)

rem /* Call sub-routine to replace the literal label string `:%~1`
rem    within this batch file by the given dynamic label name: */
call :REPLACE_LINE "%~f0" ":%%%%~1" ":%~1" || (
    rem /* In case the given label is `:REPLACE_TEXT`, display error
    rem    message, clean up temporary file and quit script: */
    >&2 echo ERROR: Label ":%~1" is already defined!
    2> nul del "%~f0.tmp"
    exit /B 1
)

rem // Perform call of the sub-routine with the dynamic label name:
call :%~1

rem /* Call sub-routine to replace the given dynamic label name
rem    within this batch file by the literal label string `:%~1`: */
call :REPLACE_LINE "%~f0" ":%~1" ":%%%%~1"

endlocal
exit /B


:REPLACE_LINE  val_file_path  val_line_LOLD  val_line_LNEW
    ::This sub-routine searches a file for a certain line
    ::case-insensitively and replaces it by another line.
    ::ARGUMENTS:
    ::  val_file_path   path to the file;
    ::  val_line_LOLD   line to search for;
    ::  val_line_LNEW   line to replace the found line;
    setlocal DisableDelayedExpansion
    rem // Store provided arguments:
    set "FILE=%~1" & rem // (path of the file to replace lines)
    set "LOLD=%~2" & rem // (line string to search for)
    set "LNEW=%~3" & rem // (line string to replace the found line)
    set "LLOC=%~0" & rem // (label of this sub-routine)
    rem // Write output to temporary file:
    > "%FILE%.tmp" (
        rem /* Read the file line by line; precede each line by a
        rem    line number and `:`, so empty lines do not appear as
        rem    empty to `for /F`, as this would ignore them: */
        for /F "delims=" %%L in ('findstr /N "^" "%FILE%"') do (
            rem // Store current line with the line number prefix:
            set "LINE=%%L"
            setlocal EnableDelayedExpansion
            rem // Check current line against search string:
            if /I "!LINE:*:=!"=="!LOLD!" (
                rem // Current line equals search string, so replace:
                echo(!LNEW!
            ) else if /I not "!LNEW!"=="!LLOC!" (
                rem // Current line is different, so keep it:
                echo(!LINE:*:=!
            ) else (
                rem /* Current line equals label of this sub-routine,
                rem    so terminate this and return with error: */
                exit /B 1
            )
            endlocal
        )
    )
    rem /* Searching and replacement finished, so move temporary file
    rem    onto original one, thus overwriting it: */
    > nul move /Y "%FILE%.tmp" "%FILE%"
    endlocal
    exit /B


:%~1
    ::This is the sub-routine with the dynamic label.
    ::Note that it must be placed after all the other code!
    echo Sub-routine.
    exit /B

基本上,它首先用作为第一个命令行参数提供的字符串替换文字标签字符串(行):%~1,然后它通过 call :%~1 调用该部分,最后,它恢复原始文字标签字符串。替换由子例程 :REPLACE_LINE.

管理