SI… O SI… en un archivo por lotes de Windows

97

¿Hay alguna manera de escribir una declaración condicional IF O IF en un archivo por lotes de Windows?

Por ejemplo:

IF [%var%] == [1] OR IF [%var%] == [2] ECHO TRUE
Mechaflash
fuente
7
No. Lo que es posible es escribir directamente una Y: IF [%var%]==[1] IF [%var2%]==[2] ECHO BOTH ARE TRUE. He utilizado para definir SET AND=IFy luego escribir: IF cond1 %AND% cond2 ECHO BOTH ARE TRUE.
Aacini

Respuestas:

95

La solución zmbq es buena, pero no se puede usar en todas las situaciones, como dentro de un bloque de código como un bucle FOR DO (...).

Una alternativa es utilizar una variable indicadora. Inicialícelo para que no esté definido y, a continuación, defínalo solo si alguna de las condiciones OR es verdadera. Luego use SI SE DEFINE como prueba final - no es necesario usar expansión retardada.

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

Podría agregar la lógica ELSE IF que utiliza arasmussen con el argumento de que podría funcionar un poco más rápido si la primera condición es verdadera, pero nunca me molesto.

Anexo : esta es una pregunta duplicada con respuestas casi idénticas a Usar un OR en una instrucción IF WinXP Batch Script

Apéndice final : casi olvido mi técnica favorita para probar si una variable es cualquiera de una lista de valores que no distinguen entre mayúsculas y minúsculas. Inicialice una variable de prueba que contenga una lista delimitada de valores aceptables, y luego use buscar y reemplazar para probar si su variable está dentro de la lista. Esto es muy rápido y utiliza un código mínimo para una lista arbitrariamente larga. Requiere expansión retardada (o el truco CALL %% VAR %%). Además, la prueba es INSENSIBLE AL CASO.

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

Lo anterior puede fallar si VAR lo contiene =, por lo que la prueba no es infalible.

Si realiza la prueba dentro de un bloque donde se necesita expansión retardada para acceder al valor actual de VAR, entonces

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 opciones como "delims =" pueden ser necesarias según los valores esperados dentro de VAR

La estrategia anterior se puede hacer confiable incluso con =VAR agregando un poco más de código.

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

Pero ahora hemos perdido la capacidad de proporcionar una cláusula ELSE a menos que agreguemos una variable indicadora. El código ha comenzado a verse un poco "feo", pero creo que es el método confiable de mejor desempeño para probar si VAR es cualquiera de un número arbitrario de opciones que no distinguen entre mayúsculas y minúsculas.

Finalmente, hay una versión más simple que creo que es un poco más lenta porque debe realizar un SI para cada valor. Aacini proporcionó esta solución en un comentario a la respuesta aceptada en el enlace antes mencionado

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

La lista de valores no puede incluir * o? caracteres y los valores y %VAR%no deben contener comillas. Las comillas dan lugar a problemas si %VAR%también contienen espacios o caracteres especiales como ^, &etc. Otra limitación de esta solución es que no proporciona la opción para una cláusula ELSE a menos que agregue una variable indicadora. Las ventajas son que puede distinguir entre mayúsculas y minúsculas o no, según la presencia o ausencia de la /Iopción IF .

dbenham
fuente
Desafortunadamente, no funcionará con valores que incluyan signos de interrogación como FOR %% A IN (-? -H -help) debido a la semántica de coincidencia de archivos.
Killroy
@Killroy: sí, ya había notado esa limitación en la respuesta. Pero su comentario me obligó a mirar la respuesta y darme cuenta de que existen limitaciones adicionales con la solución FOR. Actualicé un poco el final de la respuesta.
dbenham
¿No sería una opción un eco a findtr?
Squashman
@Squashman - Uf, sí, si quieres navegar por todos los errores y peculiaridades de FINDSTR.
dbenham
La respuesta es incorrecta. El comando atribuido a Aacini falla como se cita, pero funciona si se edita de la siguiente manera: para %% A en ("val1" "val2" "val3") hacer si "% VAR%" == %% A echo true
Ed999
54

No lo creo. Simplemente use dos IF y GOTO la misma etiqueta:

IF cond1 GOTO foundit
IF cond2 GOTO foundit

ECHO Didn't found it
GOTO end

:foundit
ECHO Found it!

:end
zmbq
fuente
Sí, por lo que puedo decir, esos dos son la única opción.
Mechaflash
1
= D De hecho, estoy en el proceso de aprender PowerShell y adaptar todos mis scripts. Pero solo necesitaba corregir un pequeño error en un script por lotes y quería saber si esto era posible. Soy exigente cuando se trata de código limpio lol
Mechaflash
1
En muchas situaciones, CALL puede ser preferible a GOTO, aunque esto solo funciona si no necesita la cláusula ELSE.
Harry Johnston
@HarryJohnston depende de si es solo un programa / script de arriba hacia abajo o si está estructurado para ejecutarse como si las funciones existieran en batch-scripting = /. En su ejemplo, GOTO sería correcto o, de lo contrario, presionaría ECHO Didn't find itincluso si lo encontrara.
Mechaflash
¿Qué tan difícil es dejar que el murciélago apoye al operador o? No se ha implementado en 2019.
Joke Huang
8

Gracias por esta publicación, me ayudó mucho.

No sé si puede ayudar, pero tuve el problema y gracias a ti encontré lo que creo que es otra forma de resolverlo en función de esta equivalencia booleana:

"A o B" es lo mismo que "no (ni A ni B)"

Así:

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

Se convierte en:

IF not [%var%] == [1] IF not [%var%] == [2] ECHO FALSE
Cactus
fuente
Pero esto solo proporciona la rama FALSE (ELSE) si ninguna de las dos es verdadera. El OP quiere la rama TRUE si alguna de las dos es verdadera.
dbenham
8

Se puede usar un simple "FOR" en una sola línea para usar una condición "o":

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

Aplicado a su caso:

FOR %%a in (1 2) DO IF %var%==%%a ECHO TRUE
Apostolos
fuente
Encontré que este es el caso más sencillo y sencillo de usar en mis scripts por lotes. ¿Cómo es que esto nunca llegó a ser la respuesta recomendada?
myusrn
Gracias. No sé, votar a favor en general es un proceso muy extraño (¿y dudoso?), No solo en stackoverflow sino también en otros foros. Pero en este caso, supongo que es el momento. La pregunta es bastante antigua y mi respuesta muy reciente. De todos modos, debemos ser felices cuando podemos encontrar una respuesta que funcione. :)
Apostolos
Un posible inconveniente de esta técnica (¡aunque me gusta lo original del enfoque!) Es que, dependiendo de la condición , puede ser posible ejecutar el comando dos veces, lo que puede que no sea necesario.
TripeHound
Teóricamente, sí. Pero he creado cientos de archivos por lotes, uso docenas de ellos todos los días y nunca conocí un caso así. ¡Así que sigamos en la práctica! :)
Apostolos
6

Incluso si esta pregunta es un poco más antigua:

Si desea usar if cond1 or cond 2, no debe usar bucles complicados o cosas así.

Proporcione ambos simples uno ifsdespués del otro combinado con goto- eso es un implícito 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

Sin goto pero una acción "in situ", puede ejecutar la acción 3 veces, si TODAS las condiciones coinciden.

perder
fuente
recién descubierto, esta respuesta ya se proporciona. debe haber supervisado eso.
Dognose el
2

Sin embargo, no hay IF <arg> ORo ELIFo ELSE IFen Lote ...

Intente anidar los otros IF dentro del ELSE del IF anterior.

IF <arg> (
    ....
) ELSE (
    IF <arg> (
        ......
    ) ELSE (
        IF <arg> (
            ....
        ) ELSE (
    )
)
Andrew Rasmussen
fuente
3
Esta no es una buena implementación de OR porque el código condicional que se ejecutará debe replicarse para cada ELSE IF. (a menos que, por supuesto, el código condicional sea GOTO, en cuyo caso tiene una forma más detallada de la solución de zmbq).
dbenham
2

Es posible utilizar una función, que evalúa la lógica OR y devuelve un solo valor.

@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
fuente
+1, esta función es un buen candidato para devolver el resultado en el código de retorno ERRORLEVEL. Entonces, la llamada puede usar la && ... || ...sintaxis conveniente directamente en lugar de la IF ... ELSE ...declaración adicional .
dbenham
2

El objetivo se puede lograr utilizando IF indirectamente.

A continuación se muestra un ejemplo de una expresión compleja que se puede escribir de manera bastante concisa y lógica en un lote de CMD, sin etiquetas incoherentes ni GOTO.

CMD maneja los bloques de código entre corchetes () como un tipo (patético) de subcapa. Cualquier código de salida que salga de un bloque se utilizará para determinar el valor verdadero / falso que el bloque reproduce en una expresión booleana más grande. Se pueden construir expresiones booleanas arbitrariamente grandes con estos bloques de código.

Ejemplo simple

Cada bloque se resuelve en verdadero (es decir, ERRORLEVEL = 0 después de que se haya ejecutado la última declaración del bloque) / falso, hasta que se haya determinado el valor de toda la expresión o el control salte (por ejemplo, a través de GOTO):

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

Ejemplo complejo

Esto resuelve el problema planteado inicialmente. Son posibles varias declaraciones en cada bloque pero en el || || || expresión es preferible ser conciso para que sea lo más legible posible. ^ es un carácter de escape en lotes de CMD y cuando se coloca al final de una línea escapará del EOL e indicará a CMD que continúe leyendo el lote actual de declaraciones en la siguiente línea.

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

Eso es para verificar si varias variables tienen el mismo valor. Aquí está para cualquiera de las variables.

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

Solo pensé en eso de la parte superior de mi cabeza. Podría compactarlo más.

coltonon
fuente
1

Me doy cuenta de que esta pregunta es antigua, pero quería publicar una solución alternativa en caso de que alguien más (como yo) encontrara este hilo mientras tenía la misma pregunta. Pude solucionar la falta de un operador OR haciendo eco de la variable y usando findtr para validar.

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

Si bien la respuesta de dbenham es bastante buena, confiar en ella IF DEFINEDpuede causarle muchos problemas si la variable que está verificando no es una variable de entorno . Las variables de script no reciben este tratamiento especial.

Si bien esto puede parecer una tontería ridícula e indocumentada, hacer una simple consulta de shell de IFwith IF /?revela que,

El condicional DEFINED funciona igual que EXIST excepto que toma un nombre de variable de entorno y devuelve verdadero si la variable de entorno está definida.

Con respecto a responder a esta pregunta, ¿hay alguna razón para no usar simplemente una bandera después de una serie de evaluaciones? Eso me parece el ORcontrol más flexible , tanto en lo que respecta a la lógica subyacente como a la legibilidad. Por ejemplo:

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)

Obviamente, pueden ser cualquier tipo de evaluación condicional, pero solo estoy compartiendo algunos ejemplos.

Si quisiera tenerlo todo en una línea, por escrito, podría simplemente encadenarlos con &&like:

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
fuente
0

Nunca llegué a existir para trabajar.

Yo uso si no existe g: xyz / what goto h: Else xcopy c: current / files g: bu / current Hay modificadores / a, etc. No estoy seguro de cuáles. Laptop en tienda. Y computadora en la oficina. No estoy ahí.

Nunca conseguí que los archivos por lotes funcionaran por encima de Windows XP

usuario8865291
fuente
Como está escrito en su ejemplo, "si no existe g: xyz / qué", xyz / qué es relativo al directorio actual de la unidad g:, que puede no ser la carpeta raíz de g. ¿Necesita "g: \ xyz \ what" aquí? Lo mismo con c: current / files y g: bu / current. Tenga en cuenta que cada unidad tiene una carpeta actual diferente y se "recuerda" incluso si esa unidad no es su directorio de trabajo actual.
John Elion
0

Una alternativa mucho más rápida que suelo usar es la siguiente, ya que puedo "o" un número arbitrario de condiciones que pueden caber en un espacio 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
fuente
-3

Al darme cuenta de que esta es una pregunta un poco vieja, las respuestas me ayudaron a encontrar una solución para probar los argumentos de la línea de comandos en un archivo por lotes; así que también quería publicar mi solución en caso de que alguien más estuviera buscando una solución similar.

Lo primero que debo señalar es que estaba teniendo problemas para que las declaraciones IF ... ELSE funcionen dentro de una cláusula FOR ... DO. Resulta que (gracias a dbenham por señalar esto inadvertidamente en sus ejemplos) la declaración ELSE no puede estar en una línea separada de los parens de cierre.

Entonces en lugar de esto:

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

Que es mi preferencia por motivos de legibilidad y estética, tienes que hacer esto:

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

Ahora la instrucción ELSE no regresa como un comando no reconocido.

Finalmente, esto es lo que estaba tratando de hacer: quería poder pasar varios argumentos a un archivo por lotes en cualquier orden, ignorando el caso e informando / fallando sobre argumentos indefinidos pasados. Así que aquí está mi solución ...

@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

Tenga en cuenta que esto solo informará sobre el primer argumento fallido. Entonces, si el usuario pasa más de un argumento inaceptable, solo se le informará sobre el primero hasta que se corrija, luego el segundo, etc.

RMWChaos
fuente
-1 Esta pregunta no tenía nada que ver con la 'sintaxis IF en un bucle FOR' ni cómo pasar argumentos correctamente, sino que tenía que ver con descubrir una forma de replicar una instrucción If .. O .. If en un archivo por lotes.
Mechaflash
1
Lo suficientemente justo. Supongo que estaba respondiendo a mi propia pregunta con la que estas respuestas me ayudaron en lugar de tener cuidado de responder la pregunta original del OP. Tendrás más cuidado en el futuro. ¡Gracias Mechaflash! -R
RMWChaos
1
Si tiene una pregunta de naturaleza similar, ha resuelto esa pregunta por su cuenta y no hay ninguna pregunta publicada en stackoverflow que sea igual a la suya, puede plantearla como una pregunta y marcar la casilla para responderla usted mismo. De esa manera estará en el sitio y la gente también podrá responder.
Mechaflash