IF ... OR IF ... em um arquivo de lote do Windows

96

Existe uma maneira de escrever uma instrução condicional IF OR IF em um arquivo em lote do Windows?

Por exemplo:

IF [%var%] == [1] OR IF [%var%] == [2] ECHO TRUE
Mechaflash
fonte
7
Não. O que é possível é escrever directamente uma E: IF [%var%]==[1] IF [%var2%]==[2] ECHO BOTH ARE TRUE. I utilizado para definir SET AND=IFe, em seguida, escrever: IF cond1 %AND% cond2 ECHO BOTH ARE TRUE.
Aacini

Respostas:

95

A solução zmbq é boa, mas não pode ser usada em todas as situações, como dentro de um bloco de código como um loop FOR DO (...).

Uma alternativa é usar uma variável indicadora. Inicialize-o como indefinido e, em seguida, defina-o apenas se qualquer uma das condições OR for verdadeira. Em seguida, use IF DEFINED como um teste final - não há necessidade de usar a expansão retardada.

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

Você poderia adicionar a lógica ELSE IF que arasmussen usa, alegando que ele pode ter um desempenho um pouco mais rápido se a primeira condição for verdadeira, mas eu nunca me incomodo.

Adendo - Esta é uma pergunta duplicada com respostas quase idênticas a Usando um OR em uma instrução IF WinXP Batch Script

Adendo final - quase esqueci minha técnica favorita de testar se uma variável faz parte de uma lista de valores que não diferenciam maiúsculas de minúsculas. Inicialize uma variável de teste contendo uma lista delimitada de valores aceitáveis ​​e, em seguida, use pesquisar e substituir para testar se sua variável está na lista. Isso é muito rápido e usa código mínimo para uma lista arbitrariamente longa. Requer expansão atrasada (ou então o truque CALL %% VAR %%). Além disso, o teste é INSENSÍVEL AO CASO.

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

O item acima pode falhar se VAR contiver =, portanto, o teste não é à prova de erros.

Se estiver fazendo o teste dentro de um bloco onde a expansão atrasada é necessária para acessar o valor atual de VAR, então

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

Opções de FOR como "delims =" podem ser necessárias dependendo dos valores esperados em VAR

A estratégia acima pode se tornar confiável mesmo =em VAR, adicionando um pouco mais de código.

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

Mas agora perdemos a capacidade de fornecer uma cláusula ELSE, a menos que adicionemos uma variável indicadora. O código começou a parecer um pouco "feio", mas acho que é o método confiável de melhor desempenho para testar se VAR é qualquer um de um número arbitrário de opções que não diferenciam maiúsculas de minúsculas.

Finalmente, há uma versão mais simples que eu acho um pouco mais lenta porque deve realizar um IF para cada valor. Aacini forneceu esta solução em um comentário à resposta aceita no link mencionado anteriormente

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

A lista de valores não pode incluir * ou? caracteres e os valores e %VAR%não deve conter aspas. Cotações levar a problemas se o %VAR%também contém espaços ou caracteres especiais como ^, &etc. Uma outra limitação com esta solução é que não oferecem a opção de uma cláusula else menos que você adicione uma variável de indicador. As vantagens são que pode ser sensível a maiúsculas ou não, dependendo da presença ou ausência da /Iopção IF .

dbenham
fonte
Infelizmente, o for não funciona com valores que incluem pontos de interrogação como FOR %% A IN (-? -H -help) devido à semântica de correspondência de arquivo.
Killroy
@Killroy - sim, já havia percebido essa limitação na resposta. Mas seu comentário me forçou a olhar para a resposta e perceber que existem limitações adicionais com a solução FOR. Eu atualizei um pouco o final da resposta.
dbenham
Um eco para findstr não seria uma opção?
Squashman de
@Squashman - Ugh, sim, se você quiser navegar por todos os bugs e peculiaridades do FINDSTR.
dbenham
A resposta está errada. O comando atribuído ao Aacini falha conforme citado, mas funciona se editado da seguinte maneira: para %% A in ("val1" "val2" "val3") faça if "% VAR%" == %% A echo true
Ed999
54

Acho que não. Basta usar dois IFs e GOTO no mesmo rótulo:

IF cond1 GOTO foundit
IF cond2 GOTO foundit

ECHO Didn't found it
GOTO end

:foundit
ECHO Found it!

:end
zmbq
fonte
Sim, pelo que posso dizer, esses dois são a única opção.
Mechaflash
1
= D Na verdade, estou no processo de aprender o PowerShell e adaptar todos os meus scripts. Mas só precisava fazer uma pequena correção de bug em um script em lote e queria saber se isso era possível. Sou exigente quando se trata de código limpo lol
Mechaflash
1
Em muitas situações, CALL pode ser preferível a GOTO, embora isso só funcione se você não precisar da cláusula ELSE.
Harry Johnston
@HarryJohnston depende se é apenas um programa / script top-down ou se está estruturado para ser executado como se as funções existissem em batch-scripting = /. Em seu exemplo, GOTO estaria correto ou então você pressionaria o ECHO Didn't find itmesmo se ele o encontrasse.
Mechaflash
É difícil deixar o bastão apoiar o operador ou? Não foi implementado em 2019.
Joke Huang
8

Obrigado por este post, me ajudou muito.

Não sei se isso pode ajudar, mas eu tive o problema e graças a você encontrei o que acho ser outra maneira de resolver com base nesta equivalência booleana:

"A ou B" é o mesmo que "não (não A e não B)"

Portanto:

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

Torna-se:

IF not [%var%] == [1] IF not [%var%] == [2] ECHO FALSE
Cacto
fonte
Mas isso só fornece a ramificação FALSE (ELSE) se nenhuma delas for verdadeira. O OP deseja a ramificação TRUE se alguma delas for verdadeira.
dbenham
8

Um simples "PARA" pode ser usado em uma única linha para usar uma condição "ou":

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

Aplicado ao seu caso:

FOR %%a in (1 2) DO IF %var%==%%a ECHO TRUE
Apostolos
fonte
Eu descobri que este é o caso mais direto e simples de usar em meus scripts em lote. Por que essa resposta nunca foi recomendada?
myusrn
Obrigado. Eu não sei, upvoting em geral é um processo muito estranho (e duvidoso?), Não apenas no stackoverflow, mas também em outros fóruns. Mas, neste caso, acho que é o momento. A pergunta é bastante antiga e minha resposta muito recente. De qualquer forma, devemos ficar felizes quando encontramos alguma resposta que funcione. :)
Apostolos
Uma possível desvantagem dessa técnica (embora eu goste da abordagem pronta para usar !) É que, dependendo da condição , pode ser possível executar o comando duas vezes, o que pode não ser desejado.
TripeHound
Teoricamente sim. Mas eu criei centenas de arquivos em lote, uso dezenas deles todos os dias e nunca encontrei um caso assim. Portanto, vamos manter a prática! :)
Apostolos
6

Mesmo que esta pergunta seja um pouco mais antiga:

Se você quiser usar if cond1 or cond 2- você não deve usar loops complicados ou coisas assim.

Simples forneça um ifsapós o outro combinado com goto- que é um ou implícito.

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

Sem ir para, mas uma ação "local", você pode executar a ação 3 vezes, se TODAS as condições forem correspondentes.

dognose
fonte
acabei de ver esta resposta já foi fornecida. deve ter supervisionado isso.
dognose
2

Não há IF <arg> ORou ELIFou ELSE IFem lote, no entanto ...

Tente aninhar os outros IFs dentro do ELSE do IF anterior.

IF <arg> (
    ....
) ELSE (
    IF <arg> (
        ......
    ) ELSE (
        IF <arg> (
            ....
        ) ELSE (
    )
)
Andrew Rasmussen
fonte
3
Esta não é uma boa implementação de OR porque o código condicional a ser executado deve ser replicado para cada ELSE IF. (a menos, é claro, que o código condicional seja um GOTO, caso em que você tem uma forma mais detalhada da solução do zmbq.)
dbenham
2

É possível usar uma função, que avalia a lógica OR e retorna um único 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
fonte
+1, esta função é uma boa candidata para retornar o resultado no código de retorno ERRORLEVEL. Então, a chamada pode usar a && ... || ...sintaxe conveniente diretamente em vez da IF ... ELSE ...instrução extra .
dbenham
2

O objetivo pode ser alcançado usando IFs indiretamente.

Abaixo está um exemplo de uma expressão complexa que pode ser escrita de forma bastante concisa e lógica em um lote CMD, sem rótulos e GOTOs incoerentes.

Blocos de código entre () colchetes são tratados pelo CMD como um tipo (patético) de subshell. Qualquer código de saída que saia de um bloco será usado para determinar o valor verdadeiro / falso que o bloco reproduz em uma expressão booleana maior. Expressões booleanas arbitrariamente grandes podem ser criadas com esses blocos de código.

Exemplo simples

Cada bloco é resolvido para verdadeiro (ou seja, ERRORLEVEL = 0 após a execução da última instrução no bloco) / falso, até que o valor de toda a expressão seja determinado ou o controle salte (por exemplo, via GOTO):

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

Exemplo complexo

Isso resolve o problema levantado inicialmente. Várias instruções são possíveis em cada bloco, mas no || || || expressão é preferível ser conciso para que seja o mais legível possível. ^ é um caractere de escape em lotes CMD e, quando colocado no final de uma linha, escapará do EOL e instruirá o CMD a continuar lendo o lote atual de instruções na próxima linha.

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

Isso é para verificar se várias variáveis ​​têm valor igual. Aqui está uma das variáveis.

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

Eu só pensei nisso de cabeça. Eu poderia compactá-lo mais.

Coltonon
fonte
1

Sei que essa pergunta é antiga, mas gostaria de postar uma solução alternativa caso alguém (como eu) tenha encontrado este tópico e tenha a mesma dúvida. Consegui contornar a falta de um operador OR ecoando a variável e usando findstr para validar.

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

Embora a resposta de dbenham seja muito boa, confiar em IF DEFINEDvocê pode ter muitos problemas se a variável que você está verificando não for uma variável de ambiente . Variáveis ​​de script não recebem este tratamento especial.

Embora isso possa parecer algum BS indocumentado ridículo, fazer uma consulta shell simples de IFcom IF /?revela que,

A condicional DEFINED funciona exatamente como EXIST, exceto que recebe um nome de variável de ambiente e retorna true se a variável de ambiente for definida.

Em relação a responder a essa pergunta, há uma razão para não usar apenas um sinalizador simples após uma série de avaliações? Essa parece a ORverificação mais flexível para mim, tanto em relação à lógica subjacente quanto à legibilidade. Por exemplo:

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, eles podem ser qualquer tipo de avaliação condicional, mas estou apenas compartilhando alguns exemplos.

Se você quiser ter tudo em uma linha, por escrito, você pode apenas encadea-los juntos &&como:

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

Nunca existiu para funcionar.

Eu uso se não existir g: xyz / what goto h: Else xcopy c: current / files g: bu / current Existem modificadores / a etc. Não tenho certeza de quais. Laptop na loja. E o computador no escritório. Eu não estou aí.

Arquivos em lote nunca funcionaram acima do Windows XP

user8865291
fonte
Conforme escrito em seu exemplo, "se não existir g: xyz / what", xyz / o que é relativo ao diretório atual da unidade g:, que pode não ser a pasta raiz de g. Você precisa de "g: \ xyz \ what" aqui? O mesmo com c: current / files eg: bu / current. Observe que cada unidade possui uma pasta atual diferente, e ela é "lembrada" mesmo se a unidade não for seu diretório de trabalho atual.
John Elion
0

Uma alternativa muito mais rápida que costumo usar é a seguinte, pois posso "ou" um número arbitrário de condições que podem caber em espaço variável

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

Percebendo que esta é uma questão um pouco antiga, as respostas me ajudaram a encontrar uma solução para testar argumentos de linha de comando em um arquivo em lote; então eu queria postar minha solução também, caso alguém mais estivesse procurando por uma solução semelhante.

A primeira coisa que devo ressaltar é que estava tendo problemas para fazer com que as instruções IF ... ELSE funcionassem dentro de uma cláusula FOR ... DO. Acontece que (graças a dbenham por inadvertidamente apontar isso em seus exemplos) a instrução ELSE não pode estar em uma linha separada dos parênteses de fechamento.

Então, em vez disso:

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

Qual é a minha preferência por razões de legibilidade e estética, você tem que fazer isso:

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

Agora, a instrução ELSE não retorna como um comando não reconhecido.

Finalmente, eis o que eu estava tentando fazer - eu queria ser capaz de passar vários argumentos para um arquivo em lote em qualquer ordem, ignorando o caso e relatando / falhando em argumentos indefinidos transmitidos. Então, aqui está minha solução ...

@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

Observe que isso reportará apenas no primeiro argumento com falha. Portanto, se o usuário transmitir mais de um argumento inaceitável, ele só será informado sobre o primeiro até que seja corrigido, depois sobre o segundo, etc.

RMWChaos
fonte
-1 Esta questão não tinha nada a ver com 'sintaxe IF em um loop FOR' nem como passar argumentos corretamente, mas tinha a ver com descobrir uma maneira de replicar uma instrução If .. Ou .. If em um arquivo em lote.
Mechaflash de
1
Justo. Acho que estava respondendo à minha própria pergunta e essas respostas me ajudaram, em vez de ter o cuidado de responder à pergunta original do OP. Teremos mais cuidado no futuro. Obrigado Mechaflash! -R
RMWChaos
1
Se você tem uma pergunta de natureza semelhante, resolveu essa pergunta sozinho e não há nenhuma pergunta postada no stackoverflow que seja igual à sua, você pode colocá-la como uma pergunta e marcar a caixa para respondê-la você mesmo. Assim, estará no site e as pessoas também poderão responder.
Mechaflash