如何从另一个 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 脚本?