SI… OU SI… dans un fichier batch Windows

96

Existe-t-il un moyen d'écrire une instruction conditionnelle IF OU IF dans un fichier batch Windows?

Par exemple:

IF [%var%] == [1] OR IF [%var%] == [2] ECHO TRUE
Méchaflash
la source
7
Non . Ce qui est possible est d'écrire directement un ET: IF [%var%]==[1] IF [%var2%]==[2] ECHO BOTH ARE TRUE. Je l' habitude de définir SET AND=IFet écrire: IF cond1 %AND% cond2 ECHO BOTH ARE TRUE.
Aacini

Réponses:

95

La solution zmbq est bonne, mais ne peut pas être utilisée dans toutes les situations, comme à l'intérieur d'un bloc de code comme une boucle FOR DO (...).

Une alternative consiste à utiliser une variable indicatrice. Initialisez-le pour qu'il ne soit pas défini, puis définissez-le uniquement si l'une des conditions OR est vraie. Utilisez ensuite IF DEFINED comme test final - pas besoin d'utiliser l'expansion retardée.

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

Vous pouvez ajouter la logique ELSE IF utilisée par arasmussen au motif qu'elle pourrait fonctionner un peu plus vite si la 1ère condition est vraie, mais je ne me dérange jamais.

Addendum - Il s'agit d'une question en double avec des réponses presque identiques à Utilisation d'un OR dans une instruction IF WinXP Batch Script

Addendum final - J'ai presque oublié ma technique préférée pour tester si une variable fait partie d'une liste de valeurs insensibles à la casse. Initialisez une variable de test contenant une liste délimitée de valeurs acceptables, puis utilisez la recherche et le remplacement pour tester si votre variable se trouve dans la liste. C'est très rapide et utilise un code minimal pour une liste arbitrairement longue. Il nécessite une expansion retardée (ou bien l'astuce CALL %% VAR %%). De plus, le test est INSENSIBLE AU CAS.

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

Ce qui précède peut échouer si VAR contient =, donc le test n'est pas infaillible.

Si vous effectuez le test dans un bloc où une expansion retardée est nécessaire pour accéder à la valeur actuelle de VAR, alors

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

Des options FOR telles que "delims =" peuvent être nécessaires en fonction des valeurs attendues dans VAR

La stratégie ci-dessus peut être rendue fiable même avec =dans VAR en ajoutant un peu plus de code.

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

Mais maintenant, nous avons perdu la capacité de fournir une clause ELSE à moins d'ajouter une variable indicatrice. Le code a commencé à paraître un peu «laid», mais je pense que c'est la méthode la plus performante et fiable pour tester si VAR est l'une des options insensibles à la casse parmi un nombre arbitraire d'options.

Enfin, il existe une version plus simple qui, à mon avis, est légèrement plus lente car elle doit effectuer un IF pour chaque valeur. Aacini a fourni cette solution dans un commentaire à la réponse acceptée dans le lien mentionné ci-dessus

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

La liste de valeurs ne peut pas inclure le * ou? caractères et les valeurs et %VAR%ne doivent pas contenir de guillemets. Les guillemets posent des problèmes si le %VAR%contient également des espaces ou des caractères spéciaux tels que ^, &etc. Une autre limitation de cette solution est qu'elle ne fournit pas l'option pour une clause ELSE sauf si vous ajoutez une variable d'indicateur. Les avantages sont qu'il peut être sensible à la casse ou insensible en fonction de la présence ou de l'absence de l' /Ioption IF .

Dbenham
la source
Malheureusement, cela ne fonctionnera pas avec les valeurs qui incluent des points d'interrogation tels que FOR %% A IN (-? -H -help) en raison de la sémantique de correspondance de fichier.
Killroy
@Killroy - oui, j'avais déjà noté cette limitation dans la réponse. Mais votre commentaire m'a obligé à regarder la réponse et à réaliser qu'il y a des limitations supplémentaires avec la solution FOR. J'ai mis à jour un peu la fin de la réponse.
dbenham
Un écho à findstr ne serait-il pas une option?
Squashman
@Squashman - Euh, oui, si vous voulez naviguer dans tous les bogues et bizarreries de FINDSTR.
dbenham
La réponse est fausse. La commande attribuée à Aacini échoue comme citée, mais fonctionne si elle est modifiée comme suit: pour %% A in ("val1" "val2" "val3") do if "% VAR%" == %% A echo true
Ed999
54

Je ne pense pas. Utilisez simplement deux IF et GOTO la même étiquette:

IF cond1 GOTO foundit
IF cond2 GOTO foundit

ECHO Didn't found it
GOTO end

:foundit
ECHO Found it!

:end
zmbq
la source
Ouais pour autant que je sache, ces deux-là sont la seule option.
Mechaflash
1
= D Je suis actuellement en train d'apprendre PowerShell et d'adapter tous mes scripts. Mais juste besoin de faire une petite correction de bogue sur un script batch et je voulais savoir si cela était possible. Je suis pointilleux quand il s'agit de nettoyer le code lol
Mechaflash
1
Dans de nombreuses situations, CALL peut être préférable à GOTO, bien que cela ne fonctionne que si vous n'avez pas besoin de la clause ELSE.
Harry Johnston
@HarryJohnston cela dépend s'il s'agit simplement d'un programme / script descendant ou s'il est structuré pour s'exécuter comme si des fonctions existaient dans batch-scripting = /. Dans son exemple, GOTO serait correct ou bien vous frapperiez le ECHO Didn't find itmême s'il le trouvait.
Mechaflash
Est-il difficile de laisser la chauve-souris soutenir l'opérateur ou? Il n'a pas été mis en œuvre en 2019.
Joke Huang
8

Merci pour ce post, cela m'a beaucoup aidé.

Je ne sais pas si cela peut aider mais j'ai eu le problème et grâce à vous, j'ai trouvé ce que je pense être un autre moyen de le résoudre basé sur cette équivalence booléenne:

"A ou B" équivaut à "pas (ni A ni B)"

Donc:

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

Devient:

IF not [%var%] == [1] IF not [%var%] == [2] ECHO FALSE
Cactus
la source
Mais cela ne fournit la branche FALSE (ELSE) que si aucune n'est vraie. L'OP veut la branche TRUE si l'une ou l'autre est vraie.
dbenham
8

Un simple "POUR" peut être utilisé sur une seule ligne pour utiliser une condition "ou":

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

Appliqué à votre cas:

FOR %%a in (1 2) DO IF %var%==%%a ECHO TRUE
Apostolos
la source
J'ai trouvé que c'était le cas le plus simple et le plus simple à utiliser dans mes scripts batch. Comment se fait-il que cela n'ait jamais été la réponse recommandée?
myusrn le
Merci. Je ne sais pas, le vote à la hausse en général est un processus très étrange (et douteux?), Non seulement dans stackoverflow mais aussi dans d'autres forums. Mais dans ce cas, je suppose que c'est le moment. La question est assez ancienne et ma réponse est récente. Quoi qu'il en soit, nous devons être heureux lorsque nous pouvons trouver une réponse qui fonctionne. :)
Apostolos
Un inconvénient possible avec cette technique (bien que j'aime le caractère original de l'approche!) Est que, selon la condition , il peut être possible d'exécuter la commande deux fois, ce qui pourrait ne pas être souhaité.
TripeHound
Théoriquement, oui. Mais j'ai créé des centaines de fichiers batch, j'en utilise des dizaines tous les jours et je n'ai jamais rencontré un tel cas. Alors restons dans la pratique! :)
Apostolos
6

Même si cette question est un peu plus ancienne:

Si vous voulez utiliser if cond1 or cond 2- vous ne devriez pas utiliser de boucles compliquées ou des trucs comme ça.

Fournissez simplement les deux l'un ifsaprès l'autre combinés avec goto- c'est un ou implicite.

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

Sans goto mais une action "inplace", vous pouvez exécuter l'action 3 fois, si TOUTES les conditions correspondent.

dognose
la source
juste repéré cette réponse est déjà fournie. doit avoir supervisé cela.
dognose le
2

Il n'y a pas de IF <arg> ORou ELIFou ELSE IFen lot, cependant ...

Essayez d'imbriquer les autres IF dans l'ELSE du IF précédent.

IF <arg> (
    ....
) ELSE (
    IF <arg> (
        ......
    ) ELSE (
        IF <arg> (
            ....
        ) ELSE (
    )
)
Andrew Rasmussen
la source
3
Ce n'est pas une bonne implémentation de OR car le code conditionnel à exécuter doit être répliqué pour chaque ELSE IF. (à moins bien sûr que le code conditionnel soit un GOTO, auquel cas vous avez une forme plus verbeuse de la solution de zmbq.)
dbenham
2

Il est possible d'utiliser une fonction qui évalue la logique OU et renvoie une seule valeur.

@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
la source
+1, Cette fonction est un bon candidat pour renvoyer le résultat dans le code de retour ERRORLEVEL. Ensuite, l'appel peut utiliser la && ... || ...syntaxe pratique directement au lieu de l' IF ... ELSE ...instruction supplémentaire .
dbenham
2

L'objectif peut être atteint en utilisant indirectement les FI.

Vous trouverez ci-dessous un exemple d'expression complexe qui peut être écrite de manière assez concise et logique dans un lot CMD, sans étiquettes et GOTO incohérents.

Les blocs de code entre crochets () sont traités par CMD comme une sorte (pathétique) de sous-shell. Quel que soit le code de sortie sortant d'un bloc, il sera utilisé pour déterminer la valeur vrai / faux que le bloc lit dans une expression booléenne plus grande. Des expressions booléennes arbitrairement grandes peuvent être créées avec ces blocs de code.

Exemple simple

Chaque bloc est résolu en vrai (c'est-à-dire ERRORLEVEL = 0 après l'exécution de la dernière instruction du bloc) / faux, jusqu'à ce que la valeur de l'expression entière ait été déterminée ou que le contrôle saute (par exemple via GOTO):

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

Exemple complexe

Cela résout le problème soulevé initialement. Plusieurs instructions sont possibles dans chaque bloc mais dans le || || || expression, il est préférable d'être concis pour qu'il soit aussi lisible que possible. ^ est un caractère d'échappement dans les lots CMD et lorsqu'il est placé à la fin d'une ligne, il échappera à l'EOL et demandera à CMD de continuer à lire le lot actuel d'instructions sur la ligne suivante.

@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
la source
1
If %x%==1 (
If %y%==1 (
:: both are equal to 1.
)
)

C'est pour vérifier si plusieurs variables sont égales à la valeur. Voici pour l'une ou l'autre variable.

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

J'ai juste pensé à ça par le haut si ma tête. Je pourrais le compacter davantage.

Coltonon
la source
1

Je me rends compte que cette question est ancienne, mais je voulais publier une solution alternative au cas où quelqu'un d'autre (comme moi) trouverait ce fil tout en ayant la même question. J'ai pu contourner le manque d'opérateur OR en faisant écho à la variable et en utilisant findstr pour valider.

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

Bien que la réponse de dbenham soit plutôt bonne, compter sur IF DEFINEDpeut vous causer beaucoup de problèmes si la variable que vous vérifiez n'est pas un environnement variable d' . Les variables de script ne bénéficient pas de ce traitement spécial.

Bien que cela puisse sembler être un BS non documenté ridicule, faire une simple requête shell IFavec IF /?révèle que,

Le conditionnel DEFINED fonctionne exactement comme EXIST sauf qu'il prend un nom de variable d' environnement et retourne true si la variable d' environnement est définie.

En ce qui concerne la réponse à cette question, y a-t-il une raison de ne pas simplement utiliser un simple drapeau après une série d'évaluations? Cela ORme semble le contrôle le plus flexible , tant en ce qui concerne la logique sous-jacente que la lisibilité. Par exemple:

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)

De toute évidence, il peut s'agir de toute sorte d'évaluation conditionnelle, mais je ne partage que quelques exemples.

Si vous vouliez tout avoir sur une seule ligne, écrite, vous pouvez simplement les enchaîner avec &&comme:

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
la source
0

Je n'ai jamais existé pour travailler.

J'utilise s'il n'existe pas g: xyz / what goto h: Sinon xcopy c: current / files g: bu / current Il y a des modificateurs / a etc. Je ne sais pas lesquels. Ordinateur portable en boutique. Et ordinateur au bureau. Je ne suis pas là.

Jamais fait fonctionner les fichiers batch au-dessus de Windows XP

user8865291
la source
Comme écrit dans votre exemple, "s'il n'existe pas g: xyz / quoi", xyz / qu'est-ce qui est relatif au répertoire courant du lecteur g:, qui peut ne pas être le dossier racine de g. Avez-vous besoin de "g: \ xyz \ quoi" ici? Idem avec c: current / files et g: bu / current. Notez que chaque lecteur a un dossier en cours différent et qu'il est "mémorisé" même si ce lecteur n'est pas votre répertoire de travail actuel.
John Elion
0

Une alternative beaucoup plus rapide que j'utilise habituellement est la suivante, comme je peux "ou" un nombre arbitraire de conditions pouvant tenir dans un espace variable

@(
  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
la source
-3

Réalisant que c'est un peu une vieille question, les réponses m'ont aidé à trouver une solution pour tester les arguments de ligne de commande dans un fichier de commandes; donc je voulais également publier ma solution au cas où quelqu'un d'autre chercherait une solution similaire.

La première chose que je devrais souligner est que j'avais du mal à faire fonctionner les instructions IF ... ELSE à l'intérieur d'une clause FOR ... DO. Il s'avère que (merci à dbenham de l'avoir signalé par inadvertance dans ses exemples) l'instruction ELSE ne peut pas être sur une ligne distincte des parenthèses de fermeture.

Donc au lieu de ça:

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

Quelle est ma préférence pour la lisibilité et les raisons esthétiques, vous devez faire ceci:

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

Désormais, l'instruction ELSE ne revient pas en tant que commande non reconnue.

Enfin, voici ce que j'essayais de faire - je voulais pouvoir passer plusieurs arguments à un fichier de commandes dans n'importe quel ordre, en ignorant la casse et en signalant / échouant sur des arguments non définis passés. Voici donc ma solution ...

@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

Notez que cela ne rapportera que le premier argument ayant échoué. Donc, si l'utilisateur passe plus d'un argument inacceptable, il ne sera informé du premier que jusqu'à ce qu'il soit corrigé, puis du second, etc.

RMWChaos
la source
-1 Cette question n'avait rien à voir avec la «syntaxe IF dans une boucle FOR» ni avec comment passer correctement les arguments, mais avait à voir avec la découverte d'un moyen de répliquer une instruction If .. Or .. If dans un fichier batch.
Mechaflash
1
C'est suffisant. Je suppose que je répondais à ma propre question que ces réponses m'ont aidé plutôt que d'être prudent pour répondre à la question initiale du PO. Sera plus prudent à l'avenir. Merci Mechaflash! -R
RMWChaos
1
Si vous avez une question de même nature, que vous l'avez résolue vous-même et qu'aucune question publiée sur stackoverflow n'est la même que la vôtre, vous pouvez la poser comme une question et cocher la case pour y répondre vous-même. De cette façon, il sera sur le site et les gens pourront également y répondre.
Mechaflash