Powershell “之间的区别”退出/b";及"| |退出/b!错误等级&引用;
我们有一堆Powershell “之间的区别”退出/b";及"| |退出/b!错误等级&引用;,powershell,batch-file,cmd,Powershell,Batch File,Cmd,我们有一堆.bat构建脚本,这些脚本由一个基于PowerShell的GitLab运行程序调用,该运行程序最近从以下方面进行了重构: program args if !errorlevel! neq 0 exit /b !errorlevel! 更简洁地说: program args || exit /b 今天我调查了一个构建作业,如果您查看错误日志,它显然失败了,但报告为成功。经过多次试验,我发现这种模式并不总是如预期的那样有效: program args || exit /b 但当前者不
.bat
构建脚本,这些脚本由一个基于PowerShell的GitLab运行程序调用,该运行程序最近从以下方面进行了重构:
program args
if !errorlevel! neq 0 exit /b !errorlevel!
更简洁地说:
program args || exit /b
今天我调查了一个构建作业,如果您查看错误日志,它显然失败了,但报告为成功。经过多次试验,我发现这种模式并不总是如预期的那样有效:
program args || exit /b
但当前者不起作用时,这似乎起了作用:
program args || exit /b !errorlevel!
我已经阅读了SO的问题和下面的陈述,但仍然不能完全解释我所观察到的 DOS联机帮助(帮助退出)没有明确说明/B参数是否退出当前脚本实例,这不一定与退出当前脚本相同。 即,如果脚本位于被调用的代码段中,则退出/B将退出调用,而不是脚本
这是我用来探索这一点的最小批处理文件:
@echo off
setlocal EnableDelayedExpansion
cmd /c "exit 99" || exit /b
:: cmd /c "exit 99" || exit /b !errorlevel!
这就是我调用批处理文件的方式(模拟基于GitLab PowerShell的运行程序调用批处理文件的方式):
以下是输出,具体取决于执行批处理文件中两行中的哪一行:
PS> & .\test.bat; $LastExitCode
0
PS> & .\test.bat; $LastExitCode
99
还有另一种获得正确行为的方法,也就是使用
CALL
从PowerShell中直接调用批处理文件:
PS> & cmd.exe /c "call .\test.bat"; $LastExitCode
99
虽然我知道这可能是从PowerShell调用批处理文件的正确方法,但从我看到的许多示例来看,这似乎不是常识。我还想知道,如果“正确的方式”,为什么PowerShell不以这种方式调用批处理文件。最后,我仍然不明白为什么在取消调用时,行为会根据是否添加而改变!错误等级
到退出/b
语句
更新:感谢到目前为止的所有讨论,但我觉得它正在消失在杂草中,这可能是我在最初的问题中过于含糊的错误。我认为我真正想要的(如果可能的话)是一个明确的声明,关于errorlevel
在以下声明中(假设)何时评估:
program || exit /b
它是否真的在早期(即在运行程序之前)进行评估,如下所示:
program || exit /b %errorlevel%
或者是延迟评估(即在程序
运行且内部错误级别
更新后执行退出
时),更类似于此:
program || exit /b !errorlevel!
因此,我并不真正想要猜测,除非,不幸的是,这是我们能做的最好的事情,在这种情况下,知道没有确定的答案或这是一个bug是我可以接受的答案:o)。让我们看看三种可能的情况:
cmd /c "exit 99" || exit /b
返回0
,因为cmd/c“exit 99”
执行正确
cmd /c "exit 99" || exit /b !errorlevel!
返回99
,因为cmd/c“退出99”
将errorlevel
设置为99,我们将返回执行cmd/c“退出99”所产生的errorlevel
返回?
-errorlevel,与解析cmd/c“exit 99”
行时一样
此时已计算“%errorlevel%”
如果delayedexpansion
未设置,则唯一的区别是!错误等级代码>场景尝试将字符串分配给错误级别,这很可能不会很好地工作
至于Powershell,这是一个在人迹罕至的道路上的街角案例。一个未经彻底测试的场景,设计者希望使用此功能执行.exe
s等。毫无疑问,即使有报道,它也不会被修复,因为有一个解决方法,即使它没有很好地暴露
我相信,这就是失败到失败的场景——一个假定能够工作的设施,因为导致它失败的正确条件很少得到满足
同样地,
echo %%%%b
这是模棱两可的。它是指echo
文本%%b
还是在元变量b
的内容前加前缀echo
?(答:后者)。并不是每天都会遇到。这种模棱两可的局面有多大可能得到解决?我们甚至无法在日期
命令上实现/u
,使其以通用格式传递日期,从而解决批次
标记上发布的绝大多数面向日期的问题。至于切换到允许date
从某个纪元日期开始交付数天的可能性,我甚至没有费心提出建议,因为尽管邀请了cmd
修改建议,但绝对没有对提供的设施做任何改动,只是用户界面的更改。当老忠实在一个锁着的文件柜里消磨时光,文件柜被锁在一个废弃的厕所里,门上有一块牌子写着“当心豹子”。解决办法
cmd /c "test1.bat" & call echo Test1 %%errorlevel%%
cmd /c "call test1.bat" & call echo call Test1 %%errorlevel%%
cmd /c "test2.bat" & call echo Test2 %%errorlevel%%
cmd /c "call test2.bat" & call echo call Test2 %%errorlevel%%
- 通过
cmd/c…`&退出
,在这种情况下,没有显式退出代码的| |退出/b
解决方案按预期工作
cmd/c.\test.bat`&exit
- 注意
`
-转义&
,这会阻止PowerShell预先解释&
。如果从不涉及shell的环境(例如从计划任务)运行命令,请省略`
- 或者,您可以使用格式
cmd/c“…&exit”
,但这需要转义作为参数一部分的任何“
字符(或使用)。如果未使用PowerShell变量或表达式,则可以选择单引号,以避免该问题:cmd/c'…&exit'
- 建议定期使用
cmd/c…`&exit
从外部调用批处理文件cmd.exe
echo %%%%b
@setlocal EnableExtensions EnableDelayedExpansion
@set prompt=$G
call :DefaultExit
@echo %ErrorLevel%
call :ExplicitExit
@echo %ErrorLevel%
@rem ErrorLevel is now 2
call :DefaultExit || echo One-liner ErrorLevel==%ErrorLevel%, not zero!
@echo %ErrorLevel%
@rem Reset the errorlevel to zero
call :DefaultExit
@echo %ErrorLevel%
call :ExplicitExit || echo One-liner ErrorLevel==!ErrorLevel!
@echo %ErrorLevel%
@exit /b
:DefaultExit
exit /b
:ExplicitExit
exit /b 2
> test
>call :DefaultExit
>exit /b
0
>call :ExplicitExit
>exit /b 2
2
>call :DefaultExit || echo One-liner ErrorLevel==2, not zero!
>exit /b
One-liner ErrorLevel==2, not zero
2
>call :DefaultExit
>exit /b
2
>call :ExplicitExit || echo One-liner ErrorLevel==!ErrorLevel!
>exit /b 2
One-liner ErrorLevel==2
2
> echo %errorlevel%
2
@echo off
(call)
exit /b
@echo off
(call)
exit /b %errorlevel%
cmd /c "test1.bat" & call echo Test1 %%errorlevel%%
cmd /c "call test1.bat" & call echo call Test1 %%errorlevel%%
cmd /c "test2.bat" & call echo Test2 %%errorlevel%%
cmd /c "call test2.bat" & call echo call Test2 %%errorlevel%%
Test1 0
call Test1 1
Test2 1
call Test2 1
<command> || call exit /b %%errorlevel%%
<command> || call :exit
...
:exit
(
(goto) 2>nul
exit /b
)