为了测试目的,我在 CMD 上获取了true
命令(作为 Git Bash 的一部分)。false
以下是基本情况:
C:\>true
C:\>echo %errorlevel%
0
C:\>false
C:\>echo %errorlevel%
1
预期上述命令及其退出代码为 ( 0
on true
,1
on 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
,且false
为false
。
那么,在不知道退出代码的情况下否定命令的最简单方法是什么?
理想情况下是在同一行。相当于!
Linux 中的某些内容(例如! true
和! false
)。
答案1
我做了一些实验,这是迄今为止我发现的最佳方法
- 创建一个 not.bat 脚本并将其放入你的 PATH 文件夹中
@echo off
if %ERRORLEVEL% EQU 0 (exit /b 1) else (exit /b 0)
- 运行此测试脚本以确认 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.
。