为什么仅在||之后才设置ErrorLevel重定向失败时的操作员? [英] Why is ErrorLevel set only after || operator upon a failed redirection?

查看:67
本文介绍了为什么仅在||之后才设置ErrorLevel重定向失败时的操作员?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Upon a failed redirection (due to a non-existent file or insufficient file access), the ErrorLevel value seems not to be set (in the following examples, file test.tmp is write-protected and file test.nil does not exist):

>>> (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:

However, as soon as the failed redirection is followed by the conditional concatenation operator ||, which is querying the exit code, the ErrorLevel becomes set to 1, unexpectedly:

>>> (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:

Interestingly, ErrorLevel remains 0 when the operator && is used:

>>> (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

使用操作符&

ErrorLevel仍为0:

ErrorLevel remains also 0 using the operator &:

>>> (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命令的退出代码):

In case both conditional concatenation operators && and || appear, ErrorLevel is set to 1 too (if || occurs before &&, both branches are executed as in the last example, but I think this just because && evaluates the exit code of the preceding echo command):

>>> (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?这一切是否只能通过(失败的)重定向来完成,因为重定向是在执行任何命令之前进行的?

So what is the connection between the ErrorLevel value and the || operator, why is ErrorLevel affected by ||? Is || copying the exit code to ErrorLevel? Is all this only possible with (failed) redirections, because such are handled before any commands are executed?

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

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)).

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

I observed the described behaviour on Windows 7 and Windows 10.

推荐答案

大约5年前,我在文件重定向中首次发现了这种不合逻辑的行为在Windows和%errorlevel%中.两个月后,我在 batch:"rd"的退出代码中发现了RD(RMDIR)命令的相同问题.错误也为0 .最后一个问题的标题实际上是令人误解的,因为发生故障的RD的返回码非零,但是ERRORLEVEL与执行命令之前的值保持不变.如果返回码确实为0,则||运算符将不会触发.

I first discovered this illogical behavior nearly 5 years ago at File redirection in Windows and %errorlevel%. Two months later I discovered the same issue with the RD (RMDIR) command at batch: Exit code for "rd" is 0 on error as well. The title of that last question is actually misleading, because the return code of a failed RD is non-zero, but the ERRORLEVEL is unchanged from whatever value existed before the command was executed. If the return code were truly 0, then the || operator would not fire.

这一切都只能通过(失败的)重定向来完成,因为这是 在执行任何命令之前进行处理?

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

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

You are correct that the redirection fails before the command is executed. And the || is responding to the non-zero return code of the redirection operation. The command (ECHO in your case) is never executed if the redirection fails.

那么,ErrorLevel值和||之间有什么联系? 运算符,为什么ErrorLevel受||影响?是||复制出口 代码到ErrorLevel?

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.返回码是瞬态的-必须在每次操作后检查它们. ERRORLEVEL是cmd.exe可以随时间持久保留重要"错误状态的方法.目的是要检测所有错误,并相应地设置ERRORLEVEL.但是,如果每次成功操作后始终将其清除为0,则ERRORLEVEL对于批处理开发人员将毫无用处.因此,cmd.exe的设计者试图对成功的命令何时清除ERRORLEVEL,以及何时保留先前值进行合理的选择.我不确定它们的选择是否明智,但我尝试在内部使用哪个cmd.exe命令清除规则中记录规则成功时将错误级别降为0吗?.

There are two different error related values that must be tracked - 1) any given command (or operation) return code (exit code), and 2) ERRORLEVEL. Return codes are transient - they must be checked after every single operation. ERRORLEVEL is the cmd.exe way to persist the "important" error states over time. The intent is for all errors to be detected, and the ERRORLEVEL is to be set accordingly. But ERRORLEVEL would be kind of useless to batch developers if it were always cleared to 0 after every successful operation. So the designers of cmd.exe attempted to make logical choices as to when a successful command clears the ERRORLEVEL, and when it preserves the prior value. I'm not sure how wise they were in their choices, but I have attempted to document the rules at Which cmd.exe internal commands clear the ERRORLEVEL to 0 upon success?.

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

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

I believe that wherever an error can occur within cmd.exe, the developers were supposed to detect the return code, set the ERRORLEVEL to non-zero upon error, and then fire any || code if it is in play. But in a few cases the developer introduced a bug by not playing by the rules. After failed redirection or failed RD, the developer(s) successfully invoked the || code, but failed to set the ERRORLEVEL properly.

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

I also believe that the developer(s) of || did some defensive programming. The ERRORLEVEL should already be set to non-zero before || code is executed. But I think the || developer wisely did not trust his/her peers, and decided to set the ERRORLEVEL within the || handler as well.

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

As to what non-zero value is used, it seems logical that || would forward the original return code value to ERRORLEVEL. This would mean that the original return code must have been stored in some temporary storage area that is distinct from ERRORLEVEL. I have two pieces of evidence that support this theory:

1)当RD失败时,||运算符将设置至少4个不同的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只是转发上一个命令/操作的返回代码.

2) The ERRORLEVEL set by || is the same value that CMD /C sets, and CMD /C simply forwards the return code of the last command/operation.

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错误:

However, there is one peculiarity that threatens to invalidate this theory. If you attempt to run a non-existent command, then you get a 9009 error:

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:-/

But if you use the || operator, or CMD /C, then the ERRORLEVEL is 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的方式来解决了这一异常.但是,||处理程序并不知道转换,因此它只是将本机返回码转发到ERRORLEVEL,从而覆盖已经存在的9009值.

I resolve this anomaly in my mind by assuming that the code responsible for setting the 9009 ERRORLEVEL in the absense of || must be doing some type of context sensitive translation to generate 9009. But the || handler is not aware of the translation, so it simply forwards the native return code to ERRORLEVEL, overwriting the 9009 value that is already there.

我不知道有没有其他命令根据是否使用||给出不同的非零ERRORLEVEL值.

I am not aware of any other commands that give different non-zero ERRORLEVEL values depending on whether || was used or not.

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

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的功能,则此行为就很有意义了.如果&&要清除保留的错误,那么保留先前的错误将不会有多大好处.

Once you accept that not all commands clear the ERRORLEVEL upon success, then this behavior makes perfect sense. It wouldn't do much good to preserve prior errors if the && were to wipe out the preserved error.

这篇关于为什么仅在||之后才设置ErrorLevel重定向失败时的操作员?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆