asc*_*pfl 10 windows cmd batch-file exit-code io-redirection
在重定向失败时(由于文件不存在或文件访问不足),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
Run Code Online (Sandbox Code Playgroud)
但是,只要失败的重定向后面跟着||查询退出代码的条件连接运算符,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
Run Code Online (Sandbox Code Playgroud)
有趣的是,使用运算符时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
Run Code Online (Sandbox Code Playgroud)
ErrorLevel仍然0使用运营商&:
>>> (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
Run Code Online (Sandbox Code Playgroud)
如果两个条件连接运算符&&和||出现,也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
Run Code Online (Sandbox Code Playgroud)
那么ErrorLevel值和||运算符之间的联系是什么,为什么会ErrorLevel受到影响||?是否||将退出代码复制到ErrorLevel?所有这些只能用于(失败的)重定向,因为这些是在执行任何命令之前处理的吗?
更奇怪的是,我不能观察到相反的行为- ErrorLevel被重置为0通过&&- ,当正确地恢复测试设置(即替代(call )的(call)(设置ErrorLevel到1最初),清除文件的只读属性test.tmp,创建文件test.nil(第一行不为空,以避免set /P设置ErrorLevel到1),以及使用文件扩展名.bat,而不是.cmd用于测试(以避免set /P重置ErrorLevel到0)).
我在Windows 7和Windows 10上观察到了所描述的行为.
差不多5年前我在Windows中的文件重定向和%errorlevel%中首次发现了这种不合逻辑的行为.两个月后,我发现了批处理 RD(RMDIR)命令的相同问题:"rd"的退出代码也是0.最后一个问题的标题实际上是误导性的,因为失败的RD的返回码是非零的,但是ERRORLEVEL与执行命令之前存在的任何值都没有变化.如果返回码真的为0,则||操作员不会触发.
所有这些只能用于(失败的)重定向,因为这些是在执行任何命令之前处理的吗?
在执行命令之前重定向失败是正确的.并且||响应重定向操作的非零返回码.如果重定向失败,则永远不会执行该命令(在您的情况下为ECHO).
那么ErrorLevel值和||之间的联系是什么 运算符,为什么ErrorLevel受||影响?是|| 将退出代码复制到ErrorLevel?
必须跟踪两个不同的错误相关值 - 1)任何给定的命令(或操作)返回代码(退出代码),以及2)ERRORLEVEL.返回代码是瞬态的 - 必须在每次操作后检查它们.ERRORLEVEL是cmd.exe随时间保持"重要"错误状态的方法.目的是检测所有错误,并相应地设置ERRORLEVEL.但是ERRORLEVEL对于批处理开发人员来说是没用的,如果它在每次成功操作后总是被清除为0.因此,cmd.exe的设计者试图在成功的命令何时清除ERRORLEVEL以及何时保留先前值时做出逻辑选择.我不确定他们的选择有多么明智,但是我试图记录cmd.exe内部命令在成功时将ERRORLEVEL清除为0的规则?.
本节的其余部分是受过教育的猜想.如果没有cmd.exe原始开发人员的通信,我认为没有明确的答案.但这就是为我提供了一个成功导航cmd.exe错误行为的精神框架.
我相信在cmd.exe中可能发生错误的地方,开发人员应该检测返回代码,出错时将ERRORLEVEL设置为非零,然后在任何||代码播放时触发.但在少数情况下,开发人员通过不遵守规则来引入错误.在重定向失败或RD失败后,开发人员成功调用了||代码,但未能正确设置ERRORLEVEL.
我也相信开发人员||做了一些防御性编程.在||执行代码之前,ERRORLEVEL应该已经设置为非零.但我认为||开发人员明智地不相信他/她的同行,并决定在||处理程序中设置ERRORLEVEL .
至于使用什么非零值,||将原始返回码值转发给ERRORLEVEL 似乎是合乎逻辑的.这意味着原始返回代码必须存储在与ERRORLEVEL不同的某个临时存储区域中.我有两个支持这个理论的证据:
1)的||操作者时RD失败时,根据错误的类型设置至少4个不同的ERRORLEVEL值.
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
Run Code Online (Sandbox Code Playgroud)
然而,有一个特点可能会使这一理论无效.如果您尝试运行不存在的命令,则会收到9009错误:
C:\test>invalidCommand
'invalidCommand' is not recognized as an internal or external command,
operable program or batch file.
C:\test>echo %errorlevel%
9009
Run Code Online (Sandbox Code Playgroud)
但是如果使用||运算符或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
Run Code Online (Sandbox Code Playgroud)
我在脑海中解决了这个异常现象,假设负责设置9009 ERRORLEVEL的代码||必须进行某种类型的上下文敏感转换才能生成9009.但是||处理程序不知道翻译,所以它只是转发本机返回代码到ERRORLEVEL,覆盖已存在的9009值.
我不知道任何其他命令根据是否||使用而给出不同的非零ERRORLEVEL值.
更奇怪的是,我无法观察到相反的行为 - 当正确恢复测试设置时,错误级别被&& - 重置为0(即,通过(调用)替换(调用)(最初将ErrorLevel设置为1),清除文件test.tmp的只读属性,创建文件test.nil(第一行不为空以避免set/P将ErrorLevel设置为1),并使用文件扩展名.bat而不是.cmd进行测试(以避免设置/ P将ErrorLevel重置为0)).
一旦你接受并非所有命令在成功时都清除了ERRORLEVEL,那么这种行为就非常有意义了.如果&&要消除保留的错误,保留先前的错误并没有多大帮助.
| 归档时间: |
|
| 查看次数: |
666 次 |
| 最近记录: |