ЕСЛИ… ИЛИ ЕСЛИ… в пакетном файле Windows

97

Есть ли способ написать условный оператор IF OR IF в пакетном файле Windows?

Например:

IF [%var%] == [1] OR IF [%var%] == [2] ECHO TRUE
Mechaflash
источник
7
Нет . Что такое возможно, чтобы непосредственно писать и: IF [%var%]==[1] IF [%var2%]==[2] ECHO BOTH ARE TRUE. Я использовал , чтобы определить , SET AND=IFа затем написать: IF cond1 %AND% cond2 ECHO BOTH ARE TRUE.
Aacini

Ответы:

95

Решение zmbq хорошее, но не может использоваться во всех ситуациях, например, внутри блока кода, такого как цикл FOR DO (...).

Альтернативой является использование индикаторной переменной. Инициализируйте его как неопределенное, а затем определите его, только если выполняется одно из условий ИЛИ. Затем используйте IF DEFINED в качестве финального теста - нет необходимости использовать отложенное расширение.

FOR ..... DO (
  set "TRUE="
  IF cond1 set TRUE=1
  IF cond2 set TRUE=1
  IF defined TRUE (
    ...
  ) else (
    ...
  )
)

Вы можете добавить логику ELSE IF, которую использует arasmussen на том основании, что она может работать немного быстрее, если 1-е условие истинно, но я никогда не беспокоюсь.

Приложение - это повторяющийся вопрос с почти идентичными ответами на использование оператора ИЛИ в операторе IF. Пакетный сценарий WinXP.

Последнее добавление - я почти забыл свой любимый метод проверки, является ли переменная одним из списков нечувствительных к регистру значений. Инициализируйте тестовую переменную, содержащую список допустимых значений с разделителями, а затем используйте поиск и замену, чтобы проверить, находится ли ваша переменная в списке. Это очень быстро и требует минимального кода для произвольно длинного списка. Это требует отложенного расширения (или трюка CALL %% VAR %%). Также тест НЕ ЧУВСТВИТЕЛЬНЫЙ.

set "TEST=;val1;val2;val3;val4;val5;"
if "!TEST:;%VAR%;=!" neq "!TEST!" (echo true) else (echo false)

Вышеупомянутое может не пройти, если VAR содержит =, поэтому тест не является надежным.

Если вы выполняете тест в блоке, где требуется отложенное расширение для доступа к текущему значению VAR, тогда

for ... do (
  set "TEST=;val1;val2;val3;val4;val5;"
  for /f %%A in (";!VAR!;") do if "!TEST:%%A=!" neq "!TEST!" (echo true) else (echo false)
)

Параметры FOR, такие как "delims =", могут потребоваться в зависимости от ожидаемых значений в VAR.

Вышеупомянутую стратегию можно сделать надежной даже =в VAR, добавив немного больше кода.

set "TEST=;val1;val2;val3;val4;val5;"
if "!TEST:;%VAR%;=!" neq "!TEST!" if "!TEST:;%VAR%;=;%VAR%;"=="!TEST!" echo true

Но теперь мы потеряли возможность предоставлять предложение ELSE, если мы не добавим индикаторную переменную. Код начал выглядеть немного «некрасиво», но я думаю, что это наиболее эффективный и надежный метод проверки, является ли VAR одним из произвольного числа вариантов без учета регистра.

Наконец, есть более простая версия, которая, на мой взгляд, немного медленнее, потому что она должна выполнять один IF для каждого значения. Aacini предоставил это решение в комментарии к принятому ответу в ранее упомянутой ссылке

for %%A in ("val1" "val2" "val3" "val4" "val5") do if "%VAR%"==%%A echo true

Список значений не может включать * или? символы, а значения и %VAR%не должны содержать кавычек. Цитаты приводят к проблемам, если они %VAR%также содержат пробелы или специальные символы, например ^, &и т. Д. Еще одно ограничение этого решения заключается в том, что оно не предоставляет возможность для предложения ELSE, если вы не добавите индикаторную переменную. Преимущества в том, что он может быть чувствительным к регистру или нечувствительным в зависимости от наличия или отсутствия /Iопции IF .

Dbenham
источник
К сожалению, for не будет работать со значениями, содержащими вопросительные знаки, например FOR %% A IN (-? -H -help), из-за семантики сопоставления файлов.
Киллрой
@Killroy - да, я уже отмечал это ограничение в ответе. Но ваш комментарий заставил меня взглянуть на ответ и понять, что у решения FOR есть дополнительные ограничения. Я немного обновил конец ответа.
dbenham
Разве эхо для findstr не было бы вариантом?
Squashman
@Squashman - Ох, да, если вы хотите ориентироваться во всех ошибках и причудах FINDSTR.
dbenham
Ответ неверный. Команда, приписываемая Aacini, не выполняется, как указано в кавычках, но работает, если отредактировать следующим образом: для %% A в ("val1" "val2" "val3") сделать, если "% VAR%" == %% A echo true
Ed999,
54

Я так не думаю. Просто используйте два IF и GOTO с той же меткой:

IF cond1 GOTO foundit
IF cond2 GOTO foundit

ECHO Didn't found it
GOTO end

:foundit
ECHO Found it!

:end
zmbq
источник
Да, насколько я могу судить, эти двое - единственный вариант.
Mechaflash
1
= D Я сейчас изучаю PowerShell и адаптирую все свои скрипты. Но просто нужно было исправить небольшую ошибку в пакетном сценарии, и я хотел знать, возможно ли это. Я придирчив, когда дело доходит до чистого кода lol
Mechaflash
1
Во многих ситуациях CALL может быть предпочтительнее GOTO, хотя это работает, только если вам не нужно предложение ELSE.
Гарри Джонстон
@HarryJohnston, это зависит от того, является ли это просто нисходящей программой / сценарием или он структурирован для запуска, как если бы функции существовали в пакетном сценарии = /. В его примере GOTO будет правильным, иначе вы бы нажали, ECHO Didn't find itдаже если он его нашел.
Mechaflash
Насколько сложно позволить летучей мыши поддерживать оператора or? Это не было реализовано в 2019 году.
Шутка Хуан
8

Спасибо за этот пост, он мне очень помог.

Не знаю, может ли это помочь, но у меня возникла проблема, и благодаря вам я нашел, как мне кажется, еще один способ ее решения, основанный на этой логической эквивалентности:

«А или Б» - это то же самое, что «не (не А и не Б)»

Таким образом:

IF [%var%] == [1] OR IF [%var%] == [2] ECHO TRUE

Становится:

IF not [%var%] == [1] IF not [%var%] == [2] ECHO FALSE
Кактус
источник
Но это обеспечивает ветвь FALSE (ELSE), только если ни одно из них не является истинным. OP хочет ветвь ИСТИНА, если хоть одна из них истинна.
dbenham
8

Простое «FOR» может использоваться в одной строке для использования условия «или»:

FOR %%a in (item1 item2 ...) DO IF {condition_involving_%%a} {execute_command}  

Применимо к вашему делу:

FOR %%a in (1 2) DO IF %var%==%%a ECHO TRUE
Апостолос
источник
Я обнаружил, что это самый простой и понятный вариант использования в моих пакетных сценариях. Почему это никогда не было рекомендованным ответом?
myusrn
Спасибо. Не знаю, голосование в целом - очень странный процесс (и сомнительный?) Не только в stackoverflow, но и на других форумах. Но в данном случае, я думаю, это время. Вопрос довольно старый, и я отвечу совсем недавно. В любом случае, мы должны быть счастливы, когда можем найти хоть какой-то ответ, который работает. :)
Апостолос
Один возможный недостаток этого метода (хотя я как и из-оф-boxness подхода!) Является то , что в зависимости от состояния , это может быть возможным , чтобы выполнить команду дважды, что не может быть желанной.
TripeHound
Теоретически да. Но я создал сотни командных файлов, я использую десятки из них каждый день и никогда не встречал такого случая. Так что давайте останемся на практике! :)
Апостолос
6

Даже если этот вопрос немного старше:

Если хотите использовать if cond1 or cond 2- не используйте сложные циклы и тому подобное.

Простое предоставление обоих ifsдруг за другом в сочетании с gotoнеявным или.

//thats an implicit IF cond1 OR cond2 OR cond3
if cond1 GOTO doit
if cond2 GOTO doit
if cond3 GOTO doit

//thats our else.
GOTO end

:doit
  echo "doing it"

:end

Без goto, но с действием «на месте», вы можете выполнить действие 3 раза, если ВСЕ условия совпадают.

догадываться
источник
только что сказал, что этот ответ уже предоставлен. Я должен следить за этим.
узнал
2

Однако в Batch нет IF <arg> ORили ELIFили ELSE IF...

Попробуйте вложить другие IF в ELSE предыдущего IF.

IF <arg> (
    ....
) ELSE (
    IF <arg> (
        ......
    ) ELSE (
        IF <arg> (
            ....
        ) ELSE (
    )
)
Эндрю Расмуссен
источник
3
Это не лучшая реализация OR, потому что выполняемый условный код должен быть реплицирован для каждого ELSE IF. (если, конечно, условный код не является GOTO, и в этом случае у вас есть более подробная форма решения zmbq.)
dbenham
2

Можно использовать функцию, которая оценивает логику ИЛИ и возвращает одно значение.

@echo off
set var1=3
set var2=5
call :logic_or orResult "'%var1%'=='4'" "'%var2%'=='5'"
if %orResult%==1 ( 
    echo At least one expression is true
) ELSE echo All expressions are false
exit /b

:logic_or <resultVar> expression1 [[expr2] ... expr-n] 
SETLOCAL
set "logic_or.result=0"
set "logic_or.resultVar=%~1"

:logic_or_loop 
if "%~2"=="" goto :logic_or_end 
if %~2 set "logic_or.result=1"
SHIFT 
goto :logic_or_loop 

:logic_or_end 
( 
  ENDLOCAL 
  set "%logic_or.resultVar%=%logic_or.result%"
  exit /b
) 
Jeb
источник
+1, эта функция является хорошим кандидатом для возврата результата в коде возврата ERRORLEVEL. Тогда вызов может использовать удобный && ... || ...синтаксис напрямую вместо дополнительного IF ... ELSE ...оператора.
dbenham
2

Цель может быть достигнута за счет косвенного использования IF.

Ниже приведен пример сложного выражения, которое можно довольно кратко и логично записать в пакете CMD без несогласованных меток и GOTO.

Блоки кода между скобками () обрабатываются CMD как (жалкая) подоболочка. Независимо от того, какой код выхода выходит из блока, он будет использоваться для определения истинного / ложного значения, которое блок воспроизводит в более крупном логическом выражении. С помощью этих блоков кода можно построить произвольно большие логические выражения.

Простой пример

Каждый блок принимает значение true (т.е. ERRORLEVEL = 0 после того, как последний оператор в блоке был выполнен) / false, пока не будет определено значение всего выражения или управление не выйдет (например, через GOTO):

 ((DIR c:\xsgdde /w) || (DIR c:\ /w)) && (ECHO -=BINGO=-)

Сложный пример

Это решает изначально возникшую проблему. В каждом блоке возможно несколько операторов, но в || || || выражение желательно быть кратким, чтобы оно было как можно более читаемым. ^ - это escape-символ в пакетах CMD, и если его поместить в конец строки, он выйдет из EOL и проинструктирует CMD продолжить чтение текущего пакета операторов на следующей строке.

@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION

(
    (CALL :ProcedureType1 a b) ^
        || (CALL :ProcedureType2 sgd) ^
            || (CALL :ProcedureType1 c c)
) ^
    && (
        ECHO -=BINGO=-
        GOTO :EOF
    )
ECHO -=no bingo for you=-
GOTO :EOF

:ProcedureType1
    IF "%~1" == "%~2" (EXIT /B 0) ELSE (EXIT /B 1)
GOTO :EOF (this line is decorative as it's never reached)

:ProcedureType2
    ECHO :ax:xa:xx:aa:|FINDSTR /I /L /C:":%~1:">nul
GOTO :EOF
Богдан
источник
1
If %x%==1 (
If %y%==1 (
:: both are equal to 1.
)
)

Это для проверки того, равны ли значения нескольких переменных. Вот для любой переменной.

If %x%==1 (
:: true
)
If %x%==0 (
If %y%==1 (
:: true
)
)
If %x%==0 (
If %y%==0 (
:: False
)
)

Я просто подумал об этом с головы до ног. Я мог бы сжать его больше.

колтонон
источник
1

Я понимаю, что этот вопрос старый, но я хотел опубликовать альтернативное решение на случай, если кто-то еще (например, я) нашел эту ветку, имея тот же вопрос. Мне удалось обойти отсутствие оператора OR, повторив переменную и используя findstr для проверки.

for /f %%v in ('echo %var% ^| findstr /x /c:"1" /c:"2"') do (
    if %errorlevel% equ 0 echo true
)
AKAJim
источник
1

Хотя ответ dbenham довольно хорош, полагаясь на IF DEFINEDнего, вы можете столкнуться с множеством проблем, если проверяемая вами переменная не является средой. переменная переменной . Переменные скрипта не обрабатываются таким специальным образом.

Хотя это может показаться нелепым недокументированным BS, выполнение простого запроса оболочки IFс помощью IF /?показывает, что

Условное выражение DEFINED работает так же, как EXIST, за исключением того, что оно принимает имя переменной среды и возвращает истину, если переменная среды определена.

Что касается ответа на этот вопрос, есть ли причина не использовать простой флаг после серии оценок? ORМне это кажется наиболее гибкой проверкой как в отношении базовой логики, так и в отношении читабельности. Например:

Set Evaluated_True=false

IF %condition_1%==true (Set Evaluated_True=true)
IF %some_string%=="desired result" (Set Evaluated_True=true)
IF %set_numerical_variable% EQ %desired_numerical_value% (Set Evaluated_True=true)

IF %Evaluated_True%==true (echo This is where you do your passing logic) ELSE (echo This is where you do your failing logic)

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

Если вы хотите, чтобы все это было написано в одной строке, вы могли бы просто связать их вместе, &&например:

Set Evaluated_True=false

IF %condition_1%==true (Set Evaluated_True=true) && IF %some_string%=="desired result" (Set Evaluated_True=true) && IF %set_numerical_variable% EQ %desired_numerical_value% (Set Evaluated_True=true)

IF %Evaluated_True%==true (echo This is where you do your passing logic) ELSE (echo This is where you do your failing logic)
kayleeFrye_onDeck
источник
0

Никогда не существовало, чтобы работать.

Я использую, если не существует g: xyz / what goto h: Else xcopy c: current / files g: bu / current Существуют модификаторы / a и т. Д. Не уверен, какие именно. Ноутбук в магазине. И компьютер в офисе. Меня там нет.

Никогда не получалось, что командные файлы работают над Windows XP

user8865291
источник
Как написано в вашем примере, «если не существует g: xyz / what», xyz / what относится к текущему каталогу диска g :, который может не быть корневой папкой g. Вам здесь нужно "g: \ xyz \ what"? То же самое с c: current / files и g: bu / current. Обратите внимание, что у каждого диска своя текущая папка, и она «запоминается», даже если этот диск не является вашим текущим рабочим каталогом.
John Elion
0

Я обычно использую гораздо более быструю альтернативу: я могу «или» произвольное количество условий, которые могут уместиться в переменном пространстве.

@(
  Echo off
  Set "_Match= 1 2 3 "
)

Set /a "var=3"

Echo:%_Match%|Find " %var% ">nul || (
  REM Code for a false condition goes here
) && (
  REM code for a true condition goes here.
)
Бен Персоник
источник
-3

Понимая, что это немного старый вопрос, ответы помогли мне придумать решение для тестирования аргументов командной строки для пакетного файла; поэтому я также хотел опубликовать свое решение на случай, если кто-то еще искал подобное решение.

Первое, что я должен отметить, это то, что у меня возникли проблемы с тем, чтобы операторы IF ... ELSE работали внутри предложения FOR ... DO. Оказывается (спасибо dbenham за непреднамеренное указание на это в его примерах) оператор ELSE не может находиться в отдельной строке от закрывающих скобок.

Итак, вместо этого:

FOR ... DO (
    IF ... (
    )
    ELSE (
    )
)

Что я предпочитаю по удобочитаемости и эстетическим соображениям, вы должны сделать это:

FOR ... DO (
    IF ... (
    ) ELSE (
    )
)

Теперь оператор ELSE не возвращается как нераспознанная команда.

Наконец, вот что я пытался сделать - я хотел иметь возможность передавать несколько аргументов в пакетный файл в любом порядке, игнорируя регистр и сообщая / сбой при передаче неопределенных аргументов. Итак, вот мое решение ...

@ECHO OFF
SET ARG1=FALSE
SET ARG2=FALSE
SET ARG3=FALSE
SET ARG4=FALSE
SET ARGS=(arg1 Arg1 ARG1 arg2 Arg2 ARG2 arg3 Arg3 ARG3)
SET ARG=

FOR %%A IN (%*) DO (
    SET TRUE=
    FOR %%B in %ARGS% DO (
        IF [%%A] == [%%B] SET TRUE=1
        )
    IF DEFINED TRUE (
        SET %%A=TRUE
        ) ELSE (
        SET ARG=%%A
        GOTO UNDEFINED
        )
    )

ECHO %ARG1%
ECHO %ARG2%
ECHO %ARG3%
ECHO %ARG4%
GOTO END

:UNDEFINED
ECHO "%ARG%" is not an acceptable argument.
GOTO END

:END

Обратите внимание, это будет сообщать только о первом неудачном аргументе. Таким образом, если пользователь передает более одного неприемлемого аргумента, ему будет рассказано только о первом, пока он не будет исправлен, затем о втором и т. Д.

RMWChaos
источник
-1 Этот вопрос не имел ничего общего ни с «синтаксисом IF в цикле FOR», ни с тем, как правильно передавать аргументы, но имел отношение к обнаружению способа репликации оператора If .. или .. If в пакетном файле.
Mechaflash
1
Справедливо. Думаю, я отвечал на свой вопрос, в котором эти ответы помогли мне, вместо того, чтобы быть осторожным, чтобы ответить на исходный вопрос ОП. Будем осторожнее в будущем. Спасибо Mechaflash! -R
RMWChaos
1
Если у вас есть вопрос, похожий по своему характеру, вы решили этот вопрос самостоятельно, и в stackoverflow нет такого же вопроса, как и ваш собственный, вы можете задать его как вопрос и установить флажок, чтобы ответить на него самостоятельно. Таким образом, он будет на сайте, и люди также смогут на него ответить.
Mechaflash