批处理 (.bat):获取第一个脚本的名称,而非当前脚本

10

我有 first.batsecond.bat 两个批处理脚本。

first.bat 包含命令:call second.bat

second.bat 包含命令:echo %~n0 (显示当前批处理文件名)

输出结果为 Second.bat,但我想要它显示调用者的文件名而不是自己的文件名。

这个是否可能?


你能接受最赞成的答案吗?我的不正确。 - CharlesB
3个回答

10

本批次检测调用脚本的名称,甚至可以检测它是否直接从命令行调用

@echo off
setlocal DisableDelayedExpansion
set "func=%~0"
for /F "delims=\" %%X in ("%func:*\=%") do set "func=%%X"
if ":" == "%func:~0,1%" (
    goto %func%
)
REM *** Get the name of the caller
(
    (goto) 2>nul
    setlocal DisableDelayedExpansion
    call set "caller=%%~f0"
    call set _caller=%%caller:*%%~f0=%%
    if defined _caller (
        set "callType=batch"
        call "%~d0\:mainFunc\..%~pnx0" %*
    ) ELSE (
        set "callType=cmd-line"
        cmd /c "call "%~d0\:mainFunc\..%~pnx0" %*"
    )
    endlocal
)
echo NEVER REACHED
exit /b

:mainFunc
echo :mainFunc of %~nx0 arg1=%1 is called from '%caller%'/%callType%
exit /b

它利用了一个(goto)语句将从堆栈中移除一级的事实。
这会导致离开当前批处理文件,%~f0将是调用脚本的名称(而%~0是该批处理程序的当前函数),或在从命令行调用时为文本%~f0

然后再次使用"%~d0\:mainFunc\..%~pnx0"调用自己的脚本。

外部脚本

为了方便使用,您可以添加一个辅助批处理文件。
在您自己的脚本中使用以下行:

@echo off
<:GetCaller <nul call GetCaller.bat myCallerVar
echo This batch was called from "%myCallerVar%"

命名帮助批处理文件为GetCaller.bat

@echo off
setlocal DisableDelayedExpansion
set "func=%~0"
for /F "delims=\" %%X in ("%func:*\=%") do set "func=%%X"
if ":" == "%func:~0,1%" (
    goto %func%
)

REM *** STEP1
REM *** Get the filename of the caller of this script, needed for later restart that
(
    (goto) 2>nul
    setlocal DisableDelayedExpansion %= it could be reenabled by the GOTO =%
    set "_returnVar=%~1"
    call set "_lastCaller=%%~f0"
    call set "_argToLastCaller=%%*"
    call "%~d0\:Step2\..%~pnx0" %*
)
exit /b %= This is never reached =%

:Step2
REM *** STEP2
REM *** Get the filename/cmd-line of the caller of the script
(
    (goto) 2>nul
    (goto) 2>nul
    setlocal DisableDelayedExpansion %= it could be reenabled by the GOTO =%    
    set "_returnVar=%_returnVar%"
    set "_lastCaller=%_lastCaller%"
    set "_argToLastCaller=%_argToLastCaller%"
    call set "caller=%%~f0"
    call set _caller=%%caller:*%%~f0=%%
    if defined _caller (
        set "callType=batch"
        call "%~d0\:Step3batch\..%~pnx0"
    ) ELSE (
        set "callType=cmd-line"
        cmd /c "call "%~d0\:Step3batch\..%~pnx0" "
    )
    endlocal
)
exit /b %= This is never reached =%

:Step3batch
REM *** STEP3 Restart the requester batch, but jump to the label :GetCaller
call :GetCaller
exit /b %= This is never reached =%

:GetCaller
REM *** This uses the trick, that starting a batch without CALL will jump to the last used label
if "%_returnVar%" NEQ "" set "%_returnVar%=%_caller%"
%_lastCaller% %_argToLastCaller%

echo #6 never reached

将其简化为仅有2或3行以嵌入其他脚本...目标只是检测调用脚本的名称...你能给一个例子或者只是要点(获取调用脚本的名称)吗? - ZEE
1
@ZEE,用2或3行代码是不可能的,因为你必须退出当前批处理程序以获取调用脚本名称,然后必须重新启动当前脚本但避免无限循环。但是,将完整的代码移动到辅助批处理文件中是可能的。 - jeb
1
@ZEE 添加了一个辅助脚本。 - jeb

6

我认为最简单的方法是将第一批文件名作为参数传递给第二批文件,像这样。

REM First.bat
call Second.bat %~n0

REM Second.bat
echo %1

希望这能帮到您!

5

在调用second.bat之前,将%~n0的值存储在一个环境变量中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接