gpt4 book ai didi

windows - 为什么仅在||之后才设置ErrorLevel重定向失败时的运算符(operator)?

转载 作者:可可西里 更新时间:2023-11-01 12:41:31 24 4
gpt4 key购买 nike

重定向失败时(由于文件不存在或文件访问不足),似乎未设置ErrorLevel值(在以下示例中,文件test.tmp受写保护,并且文件test.nil不存在):

>>> (call ) & rem // (reset `ErrorLevel`)

>>> > "test.tmp" echo Text
Access is denied.

>>> echo ErrorLevel=%ErrorLevel%
ErrorLevel=0

>>> (call ) & rem // (reset `ErrorLevel`)

>>> < "test.nil" set /P DUMMY=""
The system cannot find the file specified.

>>> echo ErrorLevel=%ErrorLevel%
ErrorLevel=0

但是,一旦失败的重定向之后是条件级联运算符 ||(正在查询退出代码), ErrorLevel就会意外地设置为 1:
>>> (call ) & rem // (reset `ErrorLevel`)

>>> (> "test.tmp" echo Text) || echo Fail
Access is denied.
Fail

>>> echo ErrorLevel=%ErrorLevel%
ErrorLevel=1

>>> (call ) & rem // (reset `ErrorLevel`)

>>> (< "test.nil" set /P DUMMY="") || echo Fail
The system cannot find the file specified.

>>> echo ErrorLevel=%ErrorLevel%
ErrorLevel=1

有趣的是,当使用运算符 ErrorLevel时, 0仍为 &&:
>>> (call ) & rem // (reset `ErrorLevel`)

>>> (> "test.tmp" echo Text) && echo Pass
Access is denied.

>>> echo ErrorLevel=%ErrorLevel%
ErrorLevel=0

>>> (call ) & rem // (reset `ErrorLevel`)

>>> (< "test.nil" set /P DUMMY="") && echo Pass
The system cannot find the file specified.

>>> echo ErrorLevel=%ErrorLevel%
ErrorLevel=0

使用运算符(operator) ErrorLevel0仍然是 &:
>>> (call ) & rem // (reset `ErrorLevel`)

>>> (> "test.tmp" echo Text) & echo Pass or Fail
Access is denied.
Pass or Fail

>>> echo ErrorLevel=%ErrorLevel%
ErrorLevel=0

>>> (call ) & rem // (reset `ErrorLevel`)

>>> (< "test.nil" set /P DUMMY="") & echo Pass or Fail
The system cannot find the file specified.
Pass or Fail

>>> echo ErrorLevel=%ErrorLevel%
ErrorLevel=0

如果同时出现条件串联运算符 &&||ErrorLevel也设置为 1(如果 ||出现在 &&之前,则两个分支都如上一个示例中那样执行,但是我认为这只是因为 &&评估了前面 echo命令的退出代码):
>>> (call ) & rem // (reset `ErrorLevel`)

>>> (> "test.tmp" echo Text) && echo Pass || echo Fail
Access is denied.
Fail

>>> echo ErrorLevel=%ErrorLevel%
ErrorLevel=1

>>> (call ) & rem // (reset `ErrorLevel`)

>>> (< "test.nil" set /P DUMMY="") || echo Fail && echo Pass
The system cannot find the file specified.
Fail
Pass

>>> echo ErrorLevel=%ErrorLevel%
ErrorLevel=1

那么 ErrorLevel值和 ||运算符之间的联系是什么,为什么 ErrorLevel||影响? ||是否将退出代码复制到 ErrorLevel?所有这些都只能通过(失败的)重定向来完成,因为重定向是在执行任何命令之前进行的?

更奇怪的是,当正确还原测试设置(即用 ErrorLevel替换 0(将 &&最初设置为 (call )),清除读取的内容时,我无法观察到相反的行为- (call)ErrorLevel重置为 1)。仅文件 test.tmp的属性,创建文件 test.nil(第一行不为空,以避免 set /PErrorLevel设置为 1),并使用文件扩展名 .bat而不是 .cmd进行测试(以避免 set /PErrorLevel重置为 0)。

我在Windows 7和Windows 10上观察到了所描述的行为。

最佳答案

我是在大约5年前在File redirection in Windows and %errorlevel%首次发现这种不合逻辑的行为的。两个月后,我在batch: Exit code for "rd" is 0 on error as well上发现了RD(RMDIR)命令的相同问题。最后一个问题的标题实际上是令人误解的,因为发生故障的RD的返回码非零,但是ERRORLEVEL与执行命令之前的值保持不变。如果返回码确实为0,则||运算符将不会触发。

Is all this only possible with (failed) redirections, because such are handled before any commands are executed?



您正确执行命令之前重定向失败。并且 ||正在响应重定向操作的非零返回码。如果重定向失败,则命令(在您的情况下为ECHO)将永远不会执行。

So what is the connection between the ErrorLevel value and the || operator, why is ErrorLevel affected by ||? Is || copying the exit code to ErrorLevel?



必须跟踪两个与错误相关的不同值-1)任何给定的命令(或操作)返回代码(退出代码),以及2)ERRORLEVEL。返回码是 transient 的-必须在每次操作后检查它们。 ERRORLEVEL是cmd.exe可以随时间持久保留“重要”错误状态的方法。目的是要检测所有错误,并相应地设置ERRORLEVEL。但是,如果每次成功操作后始终将其清除为0,则ERRORLEVEL对于批处理开发人员将毫无用处。因此,cmd.exe的设计者试图对成功的命令何时清除ERRORLEVEL,以及何时保留先前值进行合理的选择。我不确定他们的选择有多明智,但是我尝试在 Which cmd.exe internal commands clear the ERRORLEVEL to 0 upon success?上记录规则。

本节的其余部分是有根据的猜想。如果没有cmd.exe原始开发人员的沟通,我认为不可能给出明确的答案。但是,这就是给我提供了一个心理框架,以成功解决cmd.exe错误行为的困扰。

我相信,无论cmd.exe内哪里发生错误,开发人员都应该检测返回代码,在发生错误时将ERRORLEVEL设置为非零,然后在运行时触发任何 ||代码。但是在少数情况下,开发人员由于不遵守规则而引入了错误。重定向失败或RD失败后,开发人员成功调用了 ||代码,但未能正确设置ERRORLEVEL。

我还相信 ||的开发人员做了一些防御性编程。在执行 ||代码之前,ERRORLEVEL应该已经设置为非零。但是我认为 ||开发人员明智地不信任他/她的同龄人,因此决定在 ||处理程序中也设置ERRORLEVEL。

至于使用什么非零值,似乎合乎逻辑的是 ||将原始返回码值转发给ERRORLEVEL。这意味着原始返回码必须已经存储在不同于ERRORLEVEL的某个临时存储区中。我有两个证据支持这一理论:

1) The || operator sets at least 4 different ERRORLEVEL values when RD fails, depending on the type of error

2) ||设置的ERRORLEVEL与CMD/C设置的值相同,而CMD/C只是转发上一个命令/操作的返回码。
C:\test>(call )&rd .
The process cannot access the file because it is being used by another process.

C:\test>echo %errorlevel%
0

C:\test>(call )&rd . || rem
The process cannot access the file because it is being used by another process.

C:\test>echo %errorlevel%
32

C:\test>(call )&cmd /c rd .
The process cannot access the file because it is being used by another process.

C:\test>echo %errorlevel%
32

但是,有一种特殊性可能使这一理论无效。如果尝试运行不存在的命令,则会收到9009错误:
C:\test>invalidCommand
'invalidCommand' is not recognized as an internal or external command,
operable program or batch file.

C:\test>echo %errorlevel%
9009

但是,如果使用 ||运算符或CMD/C,则ERRORLEVEL为1:-/
C:\test>invalidCommand || rem
'invalidCommand' is not recognized as an internal or external command,
operable program or batch file.

C:\test>echo %errorlevel%
1

C:\test>(call )

C:\test>cmd /c invalidCommand
'invalidCommand' is not recognized as an internal or external command,
operable program or batch file.

C:\test>echo %errorlevel%
1

我通过假设负责在没有 ||的情况下设置9009 ERRORLEVEL的代码必须执行某种类型的上下文相关转换以生成9009的方式来解决此异常问题。将 native 返回码转发到ERRORLEVEL,覆盖已经存在的9009值。

我不知道有没有其他命令根据是否使用了 ||提供不同的非零ERRORLEVEL值。

Even more strangely, I could not observe the opposite behaviour -- ErrorLevel being reset to 0 by && --, when correctly reverting the test setup (that is, replacing (call ) by (call) (to set ErrorLevel to 1 initially), clearing the read-only attribute of file test.tmp, creating file test.nil (first line not empty to avoid set /P to set ErrorLevel to 1), and using file extension .bat rather than .cmd for testing (to avoid set /P to reset ErrorLevel to 0)).



一旦您接受了并非所有命令均在成功后清除了ERRORLEVEL的权限,则此行为就很有意义了。如果 ||清除了保留的错误,那么保留先前的错误并没有多大用处。

关于windows - 为什么仅在||之后才设置ErrorLevel重定向失败时的运算符(operator)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41318027/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com