如何从另一个 Windows 批处理脚本调用一个 Windows 批处理脚本并在两者中延迟扩展

Posted

技术标签:

【中文标题】如何从另一个 Windows 批处理脚本调用一个 Windows 批处理脚本并在两者中延迟扩展【英文标题】:How to call one Windows batch script from another with delayed expansion on in both 【发布时间】:2021-01-09 07:33:53 【问题描述】:

我有两个.bat 文件,都使用延迟扩展,所以我可以在for循环中设置变量。

下面的例子被大大简化了,只是为了说明问题

脚本one.bat

@echo off

setlocal enableextensions
setlocal enabledelayedexpansion

set j=0
for /L %%i in (1,1,2) do (
  set j=%%i
  set /A j=!j! + 1
  echo %%i !j!
  two.bat
  echo %%i !j!
)

脚本two.bat

@echo off

setlocal enableextensions
setlocal enabledelayedexpansion

echo Hello World

exit /B 0

two.bat 返回时,变量!j! 丢失,echo 重新打开。

J:\>one
1 2
Hello World
1 !j!

J:\>(
set j=2
 set /A j=!j! + 1
 echo 2 !j!
 two.bat
 echo 2 !j!
)
Missing operator.
2 !j!
Hello World
2 !j!

是的,我可以将 two.bat 设为 one.bat 中的子例程,但它有数百行,我不想维护相同逻辑的两个副本

我在这里错过了什么?

【问题讨论】:

你确定你的例子two.bat会返回one.bat吗?你为什么在每个脚本的开头使用两次setlocal 【参考方案1】:

您的假设是错误的,即执行从文件 two.bat 返回,因为只有当您使用 call command1 时才会出现这种情况。

批处理文件 one.bat 在括号中的代码块中运行 two.bat,该代码块已经在命令堆栈2 中,因此该块在终止执行之前(有点)完成。


你的输出完美地说明了发生了什么(因此我在这里评论它):

J:\>one              
1 2                  /* first loop iteration, the first `echo %%i !j!` in the block is
                        executed as expected; */
Hello World          /* `two.bat` is run; execution does NOT return to `one.bat`,
                        because there is NO `call`, which would put a return point onto
                        the stack! the only thing remembered and thus accomplished is
                        the current command line or parenthesised block; */
1 !j!                /* the second `echo %%i !j!` in the block is executed as it is
                        still buffered, but `two.bat` is already quit, hence implicit
                        `endlocal` commands have been executed, so all the nested
                        `setlocal` commands in your scripts are cancelled and delayed
                        expansion is disabled (default state); moreover, variable `j` is
                        no longer defined here; */
                     /* at this point, the parenthesised block, which is the loop body,
                        has been executed, hence batch file context is left and Command
                        Prompt context applies, so `@echo off` from `one.bat` does no
                        longer apply here; */
J:\>(                // this block is nothing but the echo of the second loop iteration,
set j=2              // which is still buffered;
 set /A j=!j! + 1    
 echo 2 !j!          
 two.bat             
 echo 2 !j!          
)                    
Missing operator.    /* this error message is caused by the attempt to execute
                        `set /A j=!j! + 1` (remember that delayed expansion is no longer
                        enabled and `j` is undefined); */
2 !j!                // first `echo %%i !j!` in the buffered second loop iteration;
Hello World          /* `two.bat` is run again; afterwards, batch file context is left
                        again and Command Prompt context applies; */
2 !j!                // second `echo %%i !j!` in the buffered second loop iteration;

要证明执行是在批处理文件还是命令提示符上下文下,只需将set /A j作为最后一个命令放在one.bat的循环体中,这样您将在输出1 !j!之后得到额外的输出0第二个2 !j!,因为set /A 在批处理文件上下文中不返回任何内容,但它在命令提示符上下文中输出(最后一个)结果(没有尾随换行符); 0 的值表明 j 不再设置。


1) 有几个例外:被调用的批处理文件涉及到管道,或者被for /F loop 运行和解析,因为批处理文件在这样的新cmd.exe 实例中运行2) 如果调用的批处理文件包含在具有串联命令的行中,情况也是如此,因此在执行two.bat 时,two.bat & echo Fine 之类的内容会回显Fine .

【讨论】:

以上是关于如何从另一个 Windows 批处理脚本调用一个 Windows 批处理脚本并在两者中延迟扩展的主要内容,如果未能解决你的问题,请参考以下文章

如何调用 shell 脚本并从另一个 shell 脚本传递参数

如何从另一个 python 脚本调用和显示 PYQT UI 脚本?

使用 Yarn 从另一个脚本调用一个脚本

从另一个脚本调用脚本的最佳方法是啥?

如何从另一个线程的 cpp 代码同步调用 qml 信号处理程序?

使用相对路径从另一个脚本调用一个 shell 脚本