如何在一行中否定命令的 %errorlevel%?

如何在一行中否定命令的 %errorlevel%?

为了测试目的,我在 CMD 上获取了true命令(作为 Git Bash 的一部分)。false

以下是基本情况:

C:\>true

C:\>echo %errorlevel%
0

C:\>false

C:\>echo %errorlevel%
1

预期上述命令及其退出代码为 ( 0on true1on false)。

现在,我通过以下方式否定命令:

C:\>true && false

C:\>echo %errorlevel%
1

C:\>false || true

C:\>echo %errorlevel%
0

这也按预期工作。


现在,我如何同时结合两个条件(&&||)来否定命令?

假设我不知道命令的退出代码,我想执行以下操作:

SOMECMD && false || true

这是我的尝试:

C:\>true && false || true

C:\>echo %errorlevel%
0

C:\>false || true && false

C:\>echo %errorlevel%
1

上述结果并不像预期的那样。所以true取反后 仍为true,且falsefalse

那么,在不知道退出代码的情况下否定命令的最简单方法是什么?

理想情况下是在同一行。相当于!Linux 中的某些内容(例如! true! false)。

答案1

我做了一些实验,这是迄今为止我发现的最佳方法

  1. 创建一个 not.bat 脚本并将其放入你的 PATH 文件夹中
@echo off
if %ERRORLEVEL% EQU 0 (exit /b 1) else (exit /b 0)
  1. 运行此测试脚本以确认 not.bat 脚本有效
@echo off
:: check to see if true / false commands exist or fallback to cmd.exe
:: note 9009 means MSG_DIR_BAD_COMMAND_OR_FILE according to https://stackoverflow.com/questions/23091906/official-ms-reference-for-cmd-exe-errorlevel-9009
false 2>nul
if %ERRORLEVEL% EQU 9009 (set CMDFALSE=cmd /c "exit 1") else (set CMDFALSE=false)
true 2>nul
if %ERRORLEVEL% EQU 9009 (set CMDTRUE=cmd /c "exit 0") else (set CMDTRUE=true)

:: informational
echo using '%CMDFALSE%' for false
echo using '%CMDTRUE%' for true

:: run the test
%CMDFALSE%
echo %ERRORLEVEL% - should be 1
%CMDFALSE% & not
echo %ERRORLEVEL% - should be 0

%CMDTRUE%
echo %ERRORLEVEL% - should be 0
%CMDTRUE% & not
echo %ERRORLEVEL% - should be 1

运行测试脚本时你应该看到此输出

using 'false' for false
using 'true' for true
1 - should be 1
0 - should be 0
0 - should be 0
1 - should be 1

现在您可以使用此语法来反转 ERRORLEVEL。

mycommand & not
echo %ERRORLEVEL%

或者如果你在批处理文件中使用它,请call在前面添加一个

mycommand & call not
echo %ERRORLEVEL%

答案2

&&并且||与 IF / ELSE 不同。

使用 IF / ELSE,可以保证仅触发一个分支。

但是 || 总是会响应最后执行的命令的返回代码。因此,给定一个类似 的语句cmd1 && cmd2 || cmd3,如果 cmd1 失败,或者 cmd2 失败,cmd3 就会触发。

鉴于上述情况,显然不可能仅使用&&和来否定返回代码(零到非零,非零到零) ||

显然,如果您加入 GOTO 或 CALL 等,就可以做到这一点......但我怀疑这是否是您想要的。

我假设您想要一个可以执行否定操作的内联、独立的构造。

看起来您习惯使用 %ERRORLEVEL%,但请记住 %ERRORLEVEL% 与命令的返回代码不同。例如,许多内部命令成功执行,但它们不会将 ERRORLEVEL 设置为 0,而是保持该值不变。and 运算符&&响应||真正的返回代码,而不查看 ERRORLEVEL。

我对 ERRORLEVEL 否定的想法是使用 SET /A 测试除以零。但是如果您想在设置值的同一行上检查 ERRORLEVEL,则必须使用延迟扩展。

(2>null set /a 1/!ERRORLEVEL! &&call ||call)

2>null当除以 0 时隐藏错误消息。(&&call<space>带有尾随空格)当除法成功时将 ERROLEVEL 设置为 0,并且||call(没有尾随空格)当除以 0 时将 ERRORLEVEL 设置为 1。括号使构造可以安全地内联使用。

您可以将代码保存在变量中,并像使用简单宏一样使用它。下面是一个完整的示例,演示了%not%宏的完整内联用法。

@echo off
setlocal enableDelayedExpansion

:: Note the delayed expansion requires the quoted ! to be escaped as ^! while defining the macro
set "not=(2>nul set /a 1/^!errorlevel^!&&call ||call)"

for /l %%N in (-2 1 2) do (
 call :setErrorLevel %%N & %not% && echo SUCCESS !errorlevel! || echo FAILURE !errorlevel!
 echo(
)
exit /b

:setErrorLevel
echo Testing %1
exit /b %1

重要的是,&在之前使用单个%not%,以便它始终执行。

显然你不必把所有事情都做成内联的:

@echo off
setlocal enableDelayedExpansion

:: Note the delayed expansion requires the quoted ! to be escaped as ^! while defining the macro
set "not=(2>nul set /a 1/^!errorlevel^!&&call ||call)"

for /l %%N in (-2 1 2) do (
 call :setErrorLevel %%N
 %not% 
 if errorlevel 1 (
   echo FAILURE !errorlevel! 
 ) else (
   echo SUCCESS !errorlevel!
 )
 echo(
)
exit /b

:setErrorLevel
echo Testing %1
exit /b %1

请注意,这echo(只是一种打印空白行的方法,比使用更安全echo.

相关内容