У меня есть true и false команды на моем CMD (как часть Git Bash) для целей тестирования.

Итак, вот основы:

C:\>true

C:\>echo %errorlevel%
0

C:\>false

C:\>echo %errorlevel%
1

Ожидаемые команды и их коды выхода ожидаются (0 на true , 1 на 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)

2 ответа2

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

Теперь вы можете использовать этот синтаксис для инвертирования ОШИБКИ.

mycommand & not
echo %ERRORLEVEL%

или если вы используете его в командном файле, добавьте call перед

mycommand & call not
echo %ERRORLEVEL%
1

&& и || не совпадают с IF / ELSE.

С IF / ELSE вам гарантировано, что будет срабатывать только одна ветвь.

Но || всегда будет отвечать на код возврата последней выполненной команды. Таким образом, с учетом такого заявления, как cmd1 && cmd2 || cmd3 , cmd3 будет запускаться в случае сбоя cmd1 или сбоя cmd2.

Учитывая вышесказанное, должно быть очевидно, что невозможно отменить код возврата (от нуля до нуля, от нуля до нуля), используя только && и || ,

Очевидно, это можно сделать, если вы добавите GOTO, или CALL, или ... но я сомневаюсь, что это то, что вы хотите.

Я предполагаю, что вы хотите встроенную, автономную конструкцию, которая будет выполнять отрицание.

Похоже, вам удобно использовать% ERRORLEVEL%, но имейте в виду, что% ERRORLEVEL% отличается от кода возврата команды. Например, многие внутренние команды выполняются успешно, но они не устанавливают значение ERRORLEVEL равным 0, а оставляют значение без изменений. && и || операторы отвечают на истинный код возврата и не смотрят на ОШИБКУ.

Моя идея для отрицания ERRORLEVEL - использовать тестирование SET /A для деления на ноль. Но вы должны использовать отложенное расширение, если вы хотите исследовать ERRORLEVEL в той же строке, в которой установлено значение.

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

Значение 2>null скрывает сообщение об ошибке при делении на 0. &&call<space> (с завершающим пробелом) устанавливает ERROLEVEL в 0, когда деление завершается успешно, а ||call (без завершающего пробела) устанавливает ERRORLEVEL в 1 при делении на 0. Скобки позволяют безопасно использовать конструкцию inline.

Вы можете сохранить код в переменной и использовать его как простой макрос. Ниже приведен полный пример, демонстрирующий полное использование макроса %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. ..

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .