ЯКЩО ... АБО ЯКЩО ... у командному файлі Windows


97

Чи є спосіб написати умовний оператор IF OR IF у пакетному файлі Windows?

Наприклад:

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

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, на тій підставі, що вона може виконуватись трохи швидше, якщо перша умова відповідає дійсності, але я ніколи не турбуюся.

Додаток - це повторне запитання з майже однаковими відповідями на Використання АБО у твердженні IF WinXP Batch Script

Остаточний додаток - я майже забув свою улюблену техніку, щоб перевірити, чи є якась змінна в списку значень, що не враховують регістр. Ініціалізуйте тестову змінну, що містить обмежений список допустимих значень, а потім використовуйте пошук та заміну, щоб перевірити, чи є ваша змінна в списку. Це дуже швидко і використовує мінімальний код для довільно довгого списку. Це вимагає відкладеного розширення (або інакше фокус 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)
)

ДЛЯ опцій типу "delims =" може знадобитися залежно від очікуваних значень у межах VAR

Вищевказану стратегію можна зробити надійною навіть =у VAR, додавши трохи більше коду.

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

Але тепер ми втратили можливість надати речення ELSE, якщо не додати змінну показника. Код почав виглядати трохи «потворно», але я думаю, що це найкращий надійний метод для перевірки, чи є VAR одним із довільної кількості варіантів, що не враховують регістр.

Нарешті, є простіша версія, яка, на мою думку, є дещо повільнішою, оскільки вона повинна виконувати одну ПІ для кожного значення. Aacini надав це рішення у коментарі до прийнятої відповіді у згаданому посиланні

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

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


На жаль, for не буде працювати зі значеннями, які включають знаки запитання, такі як FOR %% A IN (-? -H -help) через семантику відповідності файлу.
Killroy

@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

Так, наскільки я можу сказати, ці два є єдиним варіантом.
Mechaflash

1
= D Я фактично перебуваю в процесі вивчення PowerShell та адаптації всіх своїх сценаріїв. Але мені просто потрібно було виправити невелику помилку в пакетному сценарії і хотілося знати, чи можливо це. Я вибагливий, коли справа стосується чистого коду, ха-ха
Mechaflash

1
У багатьох ситуаціях CALL може бути кращим, ніж GOTO, хоча це працює, лише якщо вам не потрібне речення ELSE.
Harry Johnston

@HarryJohnston це залежить від того, чи це просто програма / скрипт зверху вниз, чи її структура структурована для запуску так, ніби функції існують у пакетному сценарії = /. У його прикладі GOTO буде правильним, інакше ви вдарите, ECHO Didn't find itнавіть якщо його знайдете.
Mechaflash

Наскільки складно дозволити кажану підтримати оператора або оператора? Він не був реалізований у 2019 році.
Joke Huang

8

Дякую за цей допис, це мені дуже допомогло.

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

"А або В" - це те саме, що "не (не А і не В)"

Отже:

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

Стає:

IF not [%var%] == [1] IF not [%var%] == [2] ECHO FALSE

Але це надає лише FALSE (ELSE) гілку, якщо жодне з них не відповідає дійсності. OP хоче, щоб гілка TRUE була справжньою.
dbenham

8

Просте "ЗА" може бути використано в один рядок для використання умови "або":

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, але й на інших форумах. Але в цьому випадку, я думаю, це час. Питання досить старе, і моя відповідь була нещодавно. У будь-якому випадку, ми повинні бути раді, коли зможемо знайти будь-яку відповідь, яка працює. :)
Апостолос

Одним з можливих недоліків цієї техніки (хоча мені подобається нестандартність підходу!) Є те, що залежно від умови , можливо, можна виконати команду двічі, що може бути небажаним.
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, але дії "inplace", ви можете виконати дію 3 рази, якщо ВСІ умови співпадають.


просто спотіста ця відповідь уже надана. муз підконтрольний цьому.
доноза

2

Там немає IF <arg> ORабо ELIFабо ELSE IFв пакетному режимі, проте ...

Спробуйте вкласти інші ІФ всередину ІНШОГО попереднього ІФ.

IF <arg> (
    ....
) ELSE (
    IF <arg> (
        ......
    ) ELSE (
        IF <arg> (
            ....
        ) ELSE (
    )
)

3
Це не є вдалою реалізацією АБО, оскільки умовний код, який потрібно виконати, повинен бути реплікаваний для кожної ІНШОЇ 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
) 

+1, Ця функція є гарним кандидатом для повернення результату в коді повернення ERRORLEVEL. Тоді виклик може використовувати зручний && ... || ...синтаксис безпосередньо замість зайвого IF ... ELSE ...оператора.
dbenham

2

Ціль може бути досягнута шляхом опосередкованого використання ІФ.

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

Блоки коду між () дужками обробляються CMD як (жалюгідний) вид підшелупки. Будь-який код виходу, що виходить із блоку, буде використовуватися для визначення істинного / хибного значення, яке відтворює блок у більшому логічному вираженні. За допомогою цих блоків коду можна будувати довільно великі булеві вирази.

Простий приклад

Кожен блок вирішується як true (тобто ERRORLEVEL = 0 після виконання останнього оператора в блоці) / false, поки не буде визначено значення всього виразу або елемент керування не вискочить (наприклад, за допомогою GOTO):

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

Складний приклад

Це вирішує поставлену спочатку проблему. У кожному блоці можливо декілька операторів, але в || || || вираз бажано бути стислим, щоб він був якомога читабельнішим. ^ є символом екранування в партіях 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
)

1

Незважаючи на те, що відповідь dbenham досить гарна, якщо покластися на неї, IF DEFINEDви можете створити багато проблем, якщо змінна, яку ви перевіряєте, не є середовищем змінною . Змінні сценарію не отримують такої особливої ​​обробки.

Хоча це може здатися деяким смішним бездокументарним BS, виконуючи простий запит оболонки IFз, IF /?виявляє, що,

Умовна DEFINED працює так само, як EXIST, за винятком того, що вона приймає ім'я змінної середовища і повертає true, якщо визначена змінна середовища .

Що стосується відповіді на це питання, чи є причина не просто використовувати простий прапор після серії оцінок? Це здається 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)

0

Ніколи не існував для роботи.

Я використовую, якщо не існує g: xyz / what goto h: Else xcopy c: current / files g: bu / current Є модифікатори / a тощо. Не впевнений, які саме. Ноутбук в магазині. І комп’ютер в офісі. Мене там немає.

Ніколи не отримували пакетні файли для роботи вище Windows XP


Як написано у вашому прикладі, "якщо не існує g: xyz / what", xyz / те, що відносно поточного каталогу диска g:, який не може бути кореневою папкою g. Вам тут потрібно "g: \ xyz \ what"? Те саме з c: current / files та g: bu / current. Зверніть увагу, що кожен диск має іншу поточну папку, і він "запам'ятовується", навіть якщо цей диск не є вашим поточним робочим каталогом.
Джон Еліон

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

Зверніть увагу, що це буде звіт лише про перший невдалий аргумент. Отже, якщо користувач передає більше одного неприйнятного аргументу, їм повідомлять лише про перший, поки він не буде виправлений, потім про другий і т.д.


-1 Це запитання не мало нічого спільного з 'IF синтаксисом у циклі FOR', ані тим, як правильно передавати аргументи, а з виявленням способу реплікації оператора If .. Або .. If у пакетному файлі.
Mechaflash

1
Справедливо. Думаю, я відповідав на своє власне запитання, що ці відповіді мені допомогли, а не обережно відповідав на початкове запитання ОП. Буде обережнішим у майбутньому. Дякую Mechaflash! -R
RMWЧаос

1
Якщо у вас є запитання, подібне за своєю суттю, ви вирішили це питання самостійно, а на stackoverflow немає жодного питання, яке б збігалося з вашим власним, ви можете поставити його як запитання та встановити прапорець, щоб відповісти на нього самостійно. Так воно буде на сайті, і люди також можуть на нього реагувати.
Mechaflash
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.