IF… OR IF… w pliku wsadowym systemu Windows

97

Czy istnieje sposób zapisania instrukcji warunkowej IF OR IF w pliku wsadowym systemu Windows?

Na przykład:

IF [%var%] == [1] OR IF [%var%] == [2] ECHO TRUE
Mechaflash
źródło
7
Nie. Co jest możliwe, jest bezpośrednio napisać I: IF [%var%]==[1] IF [%var2%]==[2] ECHO BOTH ARE TRUE. Użyłem do zdefiniowania SET AND=IF, a następnie napisać: IF cond1 %AND% cond2 ECHO BOTH ARE TRUE.
Aacini,

Odpowiedzi:

95

Rozwiązanie zmbq jest dobre, ale nie może być używane we wszystkich sytuacjach, takich jak wewnątrz bloku kodu, takiego jak pętla FOR DO (...).

Alternatywą jest użycie zmiennej wskaźnikowej. Zainicjuj go, aby był niezdefiniowany, a następnie zdefiniuj go tylko wtedy, gdy jeden z warunków LUB jest prawdziwy. Następnie użyj funkcji IF DEFINED jako ostatecznego testu - nie ma potrzeby używania opóźnionego rozszerzania.

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

Możesz dodać logikę ELSE IF, której używa arasmussen na tej podstawie, że może działać trochę szybciej, jeśli pierwszy warunek jest prawdziwy, ale nigdy się nie przejmuję.

Dodatek - jest to zduplikowane pytanie z prawie identycznymi odpowiedziami na temat używania OR w instrukcji IF WinXP Batch Script

Dodatek końcowy - prawie zapomniałem o mojej ulubionej technice sprawdzania, czy zmienna jest jedną z listy wartości niewrażliwych na wielkość liter. Zainicjuj zmienną testową zawierającą rozdzielaną listę dopuszczalnych wartości, a następnie użyj funkcji wyszukiwania i zamieniania, aby sprawdzić, czy zmienna znajduje się na liście. Jest to bardzo szybkie i wykorzystuje minimalny kod dla dowolnie długiej listy. Wymaga opóźnionej ekspansji (lub sztuczki CALL %% VAR %%). Test jest również niewrażliwy na wielkość liter.

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

Powyższe może się nie powieść, jeśli VAR zawiera =, więc test nie jest niezawodny .

Jeśli wykonujesz test w bloku, w którym potrzebne jest opóźnione rozwinięcie, aby uzyskać dostęp do bieżącej wartości VAR, to

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)
)

W zależności od oczekiwanych wartości w VAR mogą być potrzebne opcje FOR, takie jak „delims =”

Powyższą strategię można uczynić niezawodną nawet =w przypadku VAR, dodając nieco więcej kodu.

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

Ale teraz straciliśmy możliwość dostarczania klauzuli ELSE, chyba że dodamy zmienną wskaźnikową. Kod zaczął wyglądać trochę "brzydko", ale myślę, że jest to najlepsza niezawodna metoda testowania, jeśli VAR jest jedną z dowolnej liczby opcji bez rozróżniania wielkości liter.

Wreszcie jest prostsza wersja, która moim zdaniem jest nieco wolniejsza, ponieważ musi wykonywać jedną IF dla każdej wartości. Aacini podał to rozwiązanie w komentarzu do zaakceptowanej odpowiedzi w powyższym linku

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

Lista wartości nie może zawierać znaku * ani? znaków oraz wartości i %VAR%nie powinny zawierać cudzysłowów. Cytaty prowadzić do problemów, jeśli %VAR%zawiera spacje lub znaki specjalne, takie jak ^, &itd jednego innego ograniczenia ze jest to nie przewiduje opcję dla ELSE klauzuli chyba dodać zmienną wskaźnikową tego rozwiązania. Zaletą jest to, że może być uwzględniana wielkość liter lub niewrażliwa, w zależności od obecności lub braku /Iopcji IF .

dbenham
źródło
Niestety for nie działa z wartościami, które zawierają znaki zapytania, takie jak FOR %% A IN (-? -H -help) ze względu na semantykę dopasowania pliku.
Killroy
@Killroy - tak, zauważyłem już to ograniczenie w odpowiedzi. Ale Twój komentarz zmusił mnie do spojrzenia na odpowiedź i uświadomienia sobie, że rozwiązanie FOR ma dodatkowe ograniczenia. Zaktualizowałem nieco koniec odpowiedzi.
dbenham
Czy echo nie byłoby opcją?
Squashman
@Squashman - Ugh, tak, jeśli chcesz poruszać się po wszystkich błędach i dziwactwach FINDSTR.
dbenham
Odpowiedź jest zła. Polecenie przypisane do Aacini nie działa zgodnie z cudzysłowem, ale działa, jeśli jest edytowane w następujący sposób: dla %% A in ("val1" "val2" "val3") wykonaj, jeśli "% VAR%" == %% A echo true
Ed999
54

Nie sądzę. Po prostu użyj dwóch IF i GOTO tej samej etykiety:

IF cond1 GOTO foundit
IF cond2 GOTO foundit

ECHO Didn't found it
GOTO end

:foundit
ECHO Found it!

:end
zmbq
źródło
Tak, o ile wiem, te dwie są jedyną opcją.
Mechaflash
1
= D Jestem w trakcie uczenia się programu PowerShell i dostosowywania wszystkich moich skryptów. Ale po prostu potrzebowałem naprawić mały błąd w skrypcie wsadowym i chciałem wiedzieć, czy to jest możliwe. Jestem wybredny, jeśli chodzi o czysty kod lol
Mechaflash,
1
W wielu sytuacjach CALL może być lepsze niż GOTO, chociaż działa to tylko wtedy, gdy nie potrzebujesz klauzuli ELSE.
Harry Johnston,
@HarryJohnston, zależy to od tego, czy jest to tylko odgórny program / skrypt, czy też jego struktura działa tak, jakby funkcje istniały w skryptach wsadowych = /. W jego przykładzie GOTO byłoby poprawne, inaczej trafiłbyś ECHO Didn't find itnawet, gdyby go znalazł.
Mechaflash
Jak trudno jest pozwolić nietoperzowi wspierać operatora lub operatora? Nie został wdrożony w 2019 r.
Joke Huang
8

Dzięki za ten post, bardzo mi pomógł.

Nie wiem, czy to może pomóc, ale miałem problem i dzięki tobie znalazłem inny sposób rozwiązania go w oparciu o tę logiczną równoważność:

„A lub B” to to samo, co „nie (nie A i nie B)”

A zatem:

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

Staje się:

IF not [%var%] == [1] IF not [%var%] == [2] ECHO FALSE
Kaktus
źródło
Ale to zapewnia tylko gałąź FALSE (ELSE), jeśli żadna z nich nie jest prawdą. OP chce gałęzi TRUE, jeśli jedna z nich jest prawdziwa.
dbenham
8

Aby użyć warunku „lub”, można użyć prostego „FOR” w pojedynczym wierszu:

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

Dotyczy twojego przypadku:

FOR %%a in (1 2) DO IF %var%==%%a ECHO TRUE
Apostolos
źródło
Okazało się, że jest to najprostszy i najprostszy w użyciu przypadek w moich skryptach wsadowych. Jak to się stało, że ta odpowiedź nigdy nie okazała się zalecana?
myusrn
Dzięki. Nie wiem, generalnie upvotowanie jest bardzo dziwnym procesem (i wątpliwym?), Nie tylko w stackoverflow, ale także na innych forach. Ale w tym przypadku myślę, że to czas. Pytanie jest dość stare, a moja odpowiedź była niedawna. W każdym razie, musimy być szczęśliwi, gdy możemy znaleźć żadnej odpowiedzi, który działa. :)
Apostolos
Jeden możliwe wadą tej techniki (chociaż lubię out-of-the-boxness podejścia!) Jest to, że w zależności od stanu , to może być możliwe, aby wykonać polecenie dwukrotnie, co może nie być potrzebne.
TripeHound
Teoretycznie tak. Ale stworzyłem setki plików wsadowych, używam ich dziesiątki codziennie i nigdy nie spotkałem się z takim przypadkiem. Pozostańmy więc w praktyce! :)
Apostolos
6

Nawet jeśli to pytanie jest trochę starsze:

Jeśli chcesz używać if cond1 or cond 2- nie powinieneś używać skomplikowanych pętli lub podobnych rzeczy.

Proste dostarczanie obu ifspo sobie w połączeniu z goto- to niejawne lub.

//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

Bez działania goto, ale „inplace”, możesz wykonać akcję 3 razy, jeśli WSZYSTKIE warunki są zgodne.

dognose
źródło
właśnie zauważyłem, że ta odpowiedź jest już podana. Musst to nadzorował.
dognose
2

Nie ma IF <arg> ORani ELIFczy ELSE IFw trybie wsadowym, jednak ...

Spróbuj zagnieździć inne IF wewnątrz ELSE poprzedniego IF.

IF <arg> (
    ....
) ELSE (
    IF <arg> (
        ......
    ) ELSE (
        IF <arg> (
            ....
        ) ELSE (
    )
)
Andrew Rasmussen
źródło
3
Nie jest to dobra implementacja OR, ponieważ kod warunkowy do wykonania musi być replikowany dla każdego ELSE IF. (o ile oczywiście kod warunkowy jest to Goto, w którym to przypadku masz bardziej opisowy postaci roztworu zmbq w.)
dbenham
2

Możliwe jest użycie funkcji, która ocenia logikę OR i zwraca pojedynczą wartość.

@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
źródło
+1, ta funkcja jest dobrym kandydatem do zwracania wyniku w kodzie powrotu ERRORLEVEL. Następnie wywołanie może użyć wygodnej && ... || ...składni bezpośrednio zamiast dodatkowej IF ... ELSE ...instrukcji.
dbenham
2

Cel można osiągnąć poprzez pośrednie wykorzystanie FI.

Poniżej znajduje się przykład złożonego wyrażenia, które można zapisać dość zwięźle i logicznie w partii CMD, bez niespójnych etykiet i GOTO.

Bloki kodu w nawiasach () są traktowane przez CMD jako (żałosny) rodzaj podpowłoki. Cokolwiek kod wyjścia wychodzi z bloku, zostanie użyty do określenia wartości prawda / fałsz, którą blok odtwarza w większym wyrażeniu logicznym. Z tych bloków kodu można zbudować dowolnie duże wyrażenia logiczne.

Prosty przykład

Każdy blok jest przetwarzany na prawdę (tj. ERRORLEVEL = 0 po wykonaniu ostatniej instrukcji w bloku) / fałsz, dopóki wartość całego wyrażenia nie zostanie określona lub sterowanie wyskoczy (np. Przez GOTO):

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

Złożony przykład

To rozwiązuje problem podniesiony na początku. W każdym bloku jest możliwych wiele instrukcji, ale w || || || wyrażenie lepiej jest być zwięzłe, aby było jak najbardziej czytelne. ^ jest znakiem ucieczki w paczkach CMD i po umieszczeniu na końcu wiersza będzie oznaczał wyjście z EOL i poinstruowanie CMD, aby kontynuował czytanie bieżącej serii instrukcji w następnym wierszu.

@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
bogdan
źródło
1
If %x%==1 (
If %y%==1 (
:: both are equal to 1.
)
)

Służy do sprawdzania, czy wiele zmiennych ma taką samą wartość. Tutaj jest dla każdej zmiennej.

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

Po prostu pomyślałem o tym z góry, jeśli moja głowa. Mógłbym to bardziej zagęścić.

coltonon
źródło
1

Zdaję sobie sprawę, że to pytanie jest stare, ale chciałem opublikować alternatywne rozwiązanie na wypadek, gdyby ktoś inny (taki jak ja) znalazł ten wątek, mając to samo pytanie. Byłem w stanie obejść brak operatora OR, wywołując echo zmiennej i używając findstr do walidacji.

for /f %%v in ('echo %var% ^| findstr /x /c:"1" /c:"2"') do (
    if %errorlevel% equ 0 echo true
)
AKAJim
źródło
1

Chociaż odpowiedź dbenham jest całkiem dobra, poleganie na niej IF DEFINEDmoże przysporzyć Ci wielu kłopotów, jeśli sprawdzana zmienna nie jest zmienną środowiskową . Zmienne skryptowe nie są traktowane w ten specjalny sposób.

Chociaż może się to wydawać absurdalnym, nieudokumentowanym BS, wykonanie prostego zapytania w powłoce za IFpomocą IF /?ujawnia, że:

Warunek DEFINED działa tak samo jak EXIST, z tą różnicą, że przyjmuje nazwę zmiennej środowiskowej i zwraca wartość true, jeśli zmienna środowiskowa jest zdefiniowana.

Jeśli chodzi o odpowiedź na to pytanie, czy istnieje powód, aby nie używać prostej flagi po serii ocen? Wydaje ORmi się, że jest to najbardziej elastyczne sprawdzenie, zarówno pod względem logiki, jak i czytelności. Na przykład:

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)

Oczywiście mogą to być dowolne oceny warunkowe, ale podam tylko kilka przykładów.

Jeśli chcesz mieć to wszystko w jednej linii, pod względem pisma, możesz po prostu połączyć je razem za pomocą &&:

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
źródło
0

Nigdy nie przyszedłem do pracy.

Używam jeśli nie istnieje g: xyz / what goto h: Else xcopy c: current / files g: bu / current Istnieją modyfikatory / a itp. Nie wiem, które z nich. Laptop w sklepie. I komputer w biurze. Mnie tam nie ma.

Pliki wsadowe nigdy nie działały w systemie Windows XP

user8865291
źródło
Jak napisano w przykładzie, „jeśli nie istnieje g: xyz / what”, xyz / what jest względne w stosunku do bieżącego katalogu na dysku g:, który może nie być folderem głównym g. Czy potrzebujesz "g: \ xyz \ co" tutaj? To samo z c: current / files i g: bu / current. Zauważ, że każdy dysk ma inny bieżący folder i jest on „zapamiętywany”, nawet jeśli ten dysk nie jest Twoim bieżącym katalogiem roboczym.
John Elion,
0

O wiele szybsza alternatywa, której zwykle używam, jest następująca, ponieważ mogę "lub" dowolna liczba warunków, które mogą zmieścić się w zmiennej przestrzeni

@(
  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.
)
Ben Personick
źródło
-3

Zdając sobie sprawę, że jest to trochę stare pytanie, odpowiedzi pomogły mi wymyślić rozwiązanie testowania argumentów wiersza poleceń w pliku wsadowym; dlatego też chciałem zamieścić moje rozwiązanie na wypadek, gdyby ktoś inny szukał podobnego rozwiązania.

Pierwszą rzeczą, na którą powinienem zwrócić uwagę, jest to, że miałem problem z uzyskaniem działania instrukcji IF ... ELSE wewnątrz klauzuli FOR ... DO. Okazuje się (dzięki dbenhamowi za nieumyślne wskazanie tego w swoich przykładach), że instrukcja ELSE nie może znajdować się w oddzielnym wierszu od zamykających par.

Więc zamiast tego:

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

Które preferuję ze względu na czytelność i estetykę, musisz to zrobić:

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

Teraz instrukcja ELSE nie zwraca jako nierozpoznane polecenie.

Wreszcie, oto co próbowałem zrobić - chciałem móc przekazać kilka argumentów do pliku wsadowego w dowolnej kolejności, ignorując wielkość liter i zgłaszając / niepowodzenie w przekazywaniu niezdefiniowanych argumentów. Oto moje rozwiązanie ...

@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

Uwaga, to zgłosi tylko pierwszy nieudany argument. Jeśli więc użytkownik poda więcej niż jeden niedopuszczalny argument, zostanie mu poinformowany tylko o pierwszym, dopóki nie zostanie poprawiony, potem o drugim itd.

RMWChaos
źródło
-1 To pytanie nie miało nic wspólnego ze „składnią IF w pętli FOR” ani ze sposobem prawidłowego przekazywania argumentów, ale dotyczyło znalezienia sposobu na replikację instrukcji If .. Or .. If w pliku wsadowym.
Mechaflash,
1
Słusznie. Chyba odpowiadałem na własne pytanie, w którym pomogły mi te odpowiedzi, zamiast uważać, aby odpowiedzieć na pierwotne pytanie PO. W przyszłości będzie bardziej ostrożny. Dzięki Mechaflash! -R
RMWChaos,
1
Jeśli masz pytanie o podobnym charakterze, rozwiązałeś je samodzielnie, a na stackoverflow nie ma pytania, które jest takie samo jak Twoje, możesz zadać je jako pytanie i zaznaczyć pole, aby odpowiedzieć samodzielnie. W ten sposób będzie na stronie, a ludzie też będą mogli na nią odpowiedzieć.
Mechaflash,