Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/batch-file/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Powershell “之间的区别”退出/b";及"| |退出/b!错误等级&引用;_Powershell_Batch File_Cmd - Fatal编程技术网

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
      )