SE ... O SE ... in un file batch di Windows


96

C'è un modo per scrivere un'istruzione condizionale IF OR IF in un file batch di Windows?

Per esempio:

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

7
No. Quello che è possibile è quello di scrivere direttamente un AND: IF [%var%]==[1] IF [%var2%]==[2] ECHO BOTH ARE TRUE. Ho usato per definire SET AND=IFe poi scrivere: IF cond1 %AND% cond2 ECHO BOTH ARE TRUE.
Aacini

Risposte:


95

La soluzione zmbq è buona, ma non può essere utilizzata in tutte le situazioni, come all'interno di un blocco di codice come un ciclo FOR DO (...).

Un'alternativa è usare una variabile indicatore. Inizializzalo in modo che non sia definito, quindi definiscilo solo se una delle condizioni OR è vera. Quindi utilizzare SE DEFINITO come test finale, non è necessario utilizzare l'espansione ritardata.

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

Potresti aggiungere la logica ELSE IF che arasmussen usa sulla base del fatto che potrebbe funzionare un po 'più velocemente se la prima condizione è vera, ma non mi preoccupo mai.

Addendum - Questa è una domanda duplicata con risposte quasi identiche a Utilizzo di un OR in un'istruzione IF WinXP Batch Script

Addendum finale : ho quasi dimenticato la mia tecnica preferita per verificare se una variabile è uno qualsiasi di un elenco di valori senza distinzione tra maiuscole e minuscole. Inizializza una variabile di prova contenente un elenco delimitato di valori accettabili, quindi utilizza la funzione di ricerca e sostituzione per verificare se la variabile è inclusa nell'elenco. Questo è molto veloce e utilizza un codice minimo per un elenco arbitrariamente lungo. Richiede un'espansione ritardata (oppure il trucco CALL %% VAR %%). Anche il test è CASE INSENSITIVE.

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

Quanto sopra può fallire se VAR contiene =, quindi il test non è infallibile.

Se si esegue il test all'interno di un blocco in cui è necessaria un'espansione ritardata per accedere al valore corrente di VAR, allora

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

Opzioni FOR come "delims =" potrebbero essere necessarie a seconda dei valori previsti all'interno di VAR

La strategia di cui sopra può essere resa affidabile anche =in VAR aggiungendo un po 'più di codice.

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

Ma ora abbiamo perso la capacità di fornire una clausola ELSE a meno che non aggiungiamo una variabile indicatore. Il codice ha cominciato a sembrare un po '"brutto", ma penso che sia il metodo affidabile con le migliori prestazioni per testare se VAR è uno qualsiasi di un numero arbitrario di opzioni senza distinzione tra maiuscole e minuscole.

Infine c'è una versione più semplice che penso sia leggermente più lenta perché deve eseguire un IF per ogni valore. Aacini ha fornito questa soluzione in un commento alla risposta accettata nel collegamento prima citato

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

L'elenco dei valori non può includere * o? caratteri e i valori e %VAR%non devono contenere virgolette. Le virgolette creano problemi se %VAR%contiene anche spazi o caratteri speciali come ^, &ecc. Un altro limite di questa soluzione è che non fornisce l'opzione per una clausola ELSE a meno che non si aggiunga una variabile indicatore. I vantaggi sono che può essere sensibile al maiuscolo / minuscolo o insensibile a seconda della presenza o dell'assenza /Idell'opzione IF .


Sfortunatamente per non funziona con valori che includono punti interrogativi come FOR %% A IN (-? -H -help) a causa della semantica della corrispondenza del file.
Killroy

@ Killroy - sì, avevo già notato questa limitazione nella risposta. Ma il tuo commento mi ha costretto a guardare la risposta e rendermi conto che ci sono ulteriori limitazioni con la soluzione FOR. Ho aggiornato un po 'la fine della risposta.
dbenham

L'eco di findstr non sarebbe un'opzione?
Squashman

@Squashman - Ugh, sì, se vuoi esplorare tutti i bug e le stranezze di FINDSTR.
dbenham

La risposta è sbagliata. Il comando attribuito ad Aacini fallisce come citato, ma funziona se modificato come segue: per %% A in ("val1" "val2" "val3") do if "% VAR%" == %% A echo true
Ed999

54

Non credo proprio. Usa solo due IF e GOTO la stessa etichetta:

IF cond1 GOTO foundit
IF cond2 GOTO foundit

ECHO Didn't found it
GOTO end

:foundit
ECHO Found it!

:end

Sì, per quanto ne so, quei due sono l'unica opzione.
Mechaflash

1
= D In realtà sto imparando PowerShell e adattando tutti i miei script. Ma dovevo solo fare una piccola correzione di bug a uno script batch e volevo sapere se fosse possibile. Sono pignolo quando si tratta di pulire il codice lol
Mechaflash

1
In molte situazioni CALL può essere preferibile a GOTO, sebbene funzioni solo se non è necessaria la clausola ELSE.
Harry Johnston,

@HarryJohnston dipende se è solo un programma / script top-down o se è strutturato per essere eseguito come se esistessero funzioni in batch-scripting = /. Nel suo esempio, GOTO sarebbe corretto, altrimenti avresti colpito ECHO Didn't find itanche se lo avesse trovato.
Mechaflash

Quanto è difficile lasciare che il pipistrello supporti l'operatore or? Non è stato implementato nel 2019.
Joke Huang

8

Grazie per questo post, mi ha aiutato molto.

Non so se può aiutare ma ho avuto il problema e grazie a te ho trovato quello che penso sia un altro modo per risolverlo basato su questa equivalenza booleana:

"A o B" è uguale a "non (non A e non B)"

Quindi:

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

Diventa:

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

Ma questo fornisce solo il ramo FALSE (ELSE) se nessuno dei due è vero. L'OP vuole il ramo TRUE se uno dei due è vero.
dbenham

8

Un semplice "FOR" può essere utilizzato in una singola riga per utilizzare una condizione "o":

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

Applicato al tuo caso:

FOR %%a in (1 2) DO IF %var%==%%a ECHO TRUE

Ho trovato questo il caso più diretto e semplice da usare nei miei script batch. Come mai questo non è mai diventato la risposta consigliata?
myusrn

Grazie. Non lo so, l'upvoting in generale è un processo molto strano (e dubbio?), Non solo in stackoverflow ma anche in altri forum. Ma in questo caso, immagino sia il tempismo. La domanda è piuttosto vecchia e la mia risposta è recente. Comunque, dobbiamo essere felici quando riusciamo a trovare una risposta che funzioni. :)
Apostolos

Un possibile svantaggio di questa tecnica (sebbene mi piaccia l' out-of-the-boxness dell'approccio!) È che a seconda delle condizioni , potrebbe essere possibile eseguire il comando due volte, il che potrebbe non essere desiderato.
TripeHound

Teoricamente sì. Ma ho creato centinaia di file batch, ne uso dozzine ogni giorno e non ho mai incontrato un caso del genere. Quindi rimaniamo in pratica! :)
Apostolos

6

Anche se questa domanda è un po 'più vecchia:

Se vuoi usare if cond1 or cond 2, non dovresti usare loop complicati o cose del genere.

Fornisci entrambi ifsuno dopo l'altro in combinazione con goto- questo è un implicito o.

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

Senza goto ma un'azione "inplace", potresti eseguire l'azione 3 volte, se TUTTE le condizioni corrispondono.


appena individuato questa risposta è già fornita. dobbiamo averlo supervisionato.
dognose

2

Non c'è IF <arg> ORo ELIFo ELSE IFin Batch, tuttavia ...

Prova a nidificare gli altri IF all'interno dell'ELSE del precedente IF.

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

3
Questa non è una buona implementazione di OR perché il codice condizionale da eseguire deve essere replicato per ogni ELSE IF. (a meno che, ovviamente, il codice condizionale non sia un GOTO, nel qual caso hai una forma più dettagliata della soluzione di zmbq.)
dbenham

2

È possibile utilizzare una funzione, che valuta la logica OR e restituisce un singolo valore.

@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, Questa funzione è un buon candidato per restituire il risultato nel codice di ritorno ERRORLEVEL. Quindi la chiamata può utilizzare la && ... || ...sintassi conveniente direttamente invece IF ... ELSE ...dell'istruzione extra .
dbenham

2

L'obiettivo può essere raggiunto utilizzando gli IF indirettamente.

Di seguito è riportato un esempio di un'espressione complessa che può essere scritta in modo abbastanza conciso e logico in un batch CMD, senza etichette incoerenti e GOTO.

I blocchi di codice tra parentesi quadre () sono gestiti da CMD come un tipo (patetico) di subshell. Qualunque codice di uscita provenga da un blocco verrà utilizzato per determinare il valore vero / falso che il blocco riproduce in un'espressione booleana più grande. Con questi blocchi di codice è possibile costruire espressioni booleane arbitrariamente grandi.

Semplice esempio

Ogni blocco viene risolto in true (ovvero ERRORLEVEL = 0 dopo che l'ultima istruzione nel blocco è stata eseguita) / false, fino a quando il valore dell'intera espressione non è stato determinato o il controllo salta fuori (ad esempio tramite GOTO):

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

Esempio complesso

Questo risolve il problema sollevato inizialmente. Sono possibili più istruzioni in ogni blocco ma nel campo || || || è preferibile essere conciso in modo che sia il più leggibile possibile. ^ è un carattere di escape nei batch CMD e quando viene posizionato alla fine di una riga uscirà dall'EOL e istruirà CMD a continuare a leggere il batch corrente di istruzioni sulla riga successiva.

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

Serve per verificare se più variabili hanno un valore uguale. Ecco per entrambe le variabili.

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

Ho solo pensato a questo dall'alto se la mia testa. Potrei compattarlo di più.


1

Mi rendo conto che questa domanda è vecchia, ma volevo pubblicare una soluzione alternativa nel caso in cui qualcun altro (come me) avesse trovato questo thread pur avendo la stessa domanda. Sono stato in grado di aggirare la mancanza di un operatore OR facendo eco alla variabile e utilizzando findstr per convalidare.

for /f %%v in ('echo %var% ^| findstr /x /c:"1" /c:"2"') do (
    if %errorlevel% equ 0 echo true
)

1

Mentre la risposta di dbenham è abbastanza buona, fare affidamento su IF DEFINEDpuò metterti in un sacco di guai se la variabile che stai controllando non è un ambiente variabile di . Le variabili di script non ricevono questo trattamento speciale.

Anche se questo potrebbe sembrare un ridicolo BS non documentato, facendo una semplice query di shell IFconIF /? rivela che,

Il condizionale DEFINED funziona proprio come EXIST tranne per il fatto che richiede un ambiente nome di variabile di e restituisce true se la variabile di ambiente è definita.

Per quanto riguarda la risposta a questa domanda, c'è un motivo per non utilizzare solo un semplice flag dopo una serie di valutazioni? Questo mi sembra il ORcontrollo più flessibile , sia per quanto riguarda la logica sottostante che la leggibilità. Per esempio:

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)

Ovviamente, possono essere qualsiasi tipo di valutazione condizionale, ma sto solo condividendo alcuni esempi.

Se vuoi avere tutto su una riga, per quanto riguarda la scrittura, potresti semplicemente incatenarli insieme con &&come:

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

Non sono mai esistito per funzionare.

Uso se non esiste g: xyz / what goto h: Altrimenti xcopy c: current / files g: bu / current Ci sono modificatori / a ecc. Non sono sicuro di quali. Computer portatile in negozio. E il computer in ufficio. Non sono li.

Non è mai stato possibile far funzionare i file batch sopra Windows XP


Come scritto nel tuo esempio, "se non esiste g: xyz / cosa", xyz / cosa è relativo alla directory corrente dell'unità g:, che potrebbe non essere la cartella principale di g. Hai bisogno di "g: \ xyz \ what" qui? Lo stesso con c: current / files eg: bu / current. Notare che ogni unità ha una cartella corrente diversa e viene "ricordata" anche se tale unità non è la directory di lavoro corrente.
John Elion,

0

Un'alternativa molto più veloce che di solito uso è la seguente, poiché posso "o" un numero arbitrario di condizioni che possono adattarsi allo spazio variabile

@(
  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

Rendendomi conto che questa è una vecchia domanda, le risposte mi hanno aiutato a trovare una soluzione per testare gli argomenti della riga di comando in un file batch; quindi volevo pubblicare anche la mia soluzione nel caso in cui qualcun altro stesse cercando una soluzione simile.

La prima cosa che dovrei sottolineare è che stavo avendo problemi a far funzionare le istruzioni IF ... ELSE all'interno di una clausola FOR ... DO. Risulta (grazie a dbenham per aver inavvertitamente sottolineato questo nei suoi esempi) l'affermazione ELSE non può essere su una riga separata dalle parentesi di chiusura.

Quindi, invece di questo:

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

Qual è la mia preferenza per la leggibilità e per motivi estetici, devi farlo:

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

Ora l'istruzione ELSE non ritorna come un comando non riconosciuto.

Infine, ecco cosa stavo tentando di fare: volevo essere in grado di passare diversi argomenti a un file batch in qualsiasi ordine, ignorando maiuscole e minuscole e segnalando / fallendo su argomenti non definiti passati. Quindi ecco la mia soluzione ...

@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

Nota, questo riporterà solo il primo argomento fallito. Quindi, se l'utente passa più di un argomento inaccettabile, verrà informato solo del primo fino a quando non viene corretto, quindi del secondo, ecc.


-1 Questa domanda non aveva nulla a che fare con la "sintassi IF in un ciclo FOR" né con come passare correttamente gli argomenti, ma aveva a che fare con la scoperta di un modo per replicare un'istruzione If .. Or .. If in un file batch.
Mechaflash

1
Giusto. Immagino che stavo rispondendo alla mia domanda che queste risposte mi hanno aiutato piuttosto che stare attento a rispondere alla domanda originale dell'OP. Starò più attento in futuro. Grazie Mechaflash! -R
RMWChaos

1
Se hai una domanda di natura simile, hai risolto quella domanda da solo e non ci sono domande pubblicate su stackoverflow che siano uguali alla tua, puoi porla come una domanda e selezionare la casella per rispondere tu stesso. In questo modo sarà sul sito e anche le persone potranno rispondere.
Mechaflash
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.