在Windows批处理文件中,IF... OR IF...的含义是什么?

128
有没有办法在 Windows 批处理文件中编写 IF OR IF 条件语句?
例如:
IF [%var%] == [1] OR IF [%var%] == [2] ECHO TRUE

9
可以直接写一个AND语句:IF [%var%]==[1] IF [%var2%]==[2] ECHO BOTH ARE TRUE。以前我使用过定义SET AND=IF,然后编写:IF cond1 %AND% cond2 ECHO BOTH ARE TRUE的方式。 - Aacini
这个回答解决了你的问题吗?DOS批处理中的逻辑运算符(“and”,“or”) - Toni
1
SET AND=IF 这个技巧不错 :-) 但是不要试图在这种伪逻辑操作之后使用ELSE :-) - Alexander Gavriliuk
19个回答

114

zmbq的解决方案不错,但不能在所有情况下使用,比如在代码块中,比如FOR DO(...)循环。

另一种替代方法是使用指示变量。将其初始化为未定义,只有在任何一个OR条件为真时才定义它。然后使用IF DEFINED作为最终测试 - 不需要使用延迟扩展。

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

你可以添加类似于arasmussen使用的ELSE IF逻辑,因为如果第一个条件为真,它可能会稍微快一点,但我从不费心。

补充说明 - 这是一个重复的问题,几乎与在WinXP批处理脚本中使用OR的IF语句具有相同的答案。

最后补充 - 我差点忘记了我的最喜欢的测试变量是否为任何一组不区分大小写的值的技巧。初始化一个包含可接受值列表的测试变量,然后使用搜索和替换来测试您的变量是否在列表内。这非常快速,并且对于任意长的列表使用极少的代码。它需要延迟展开(或者使用CALL %%VAR%%技巧)。此外,测试不区分大小写。

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

如果 VAR 包含 =,那么上述方法可能会失败,因此测试并不是万无一失的。

如果在需要延迟扩展以访问 VAR 的当前值的块中进行测试,则:

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

根据 VAR 中预期的值,可能需要像 "delims=" 这样的选项。

通过添加更多的代码,即使在 VAR 中含有 =,也可以使上述策略变得可靠。

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

但是现在我们失去了提供ELSE子句的能力,除非我们添加一个指示变量。代码开始看起来有点“丑陋”,但我认为这是用于测试VAR是否为任意大小写选项之一的最佳性能可靠方法。

最后有一个更简单的版本,我认为它稍微慢一些,因为它必须为每个值执行一个IF。Aacini在上述链接的已接受答案的评论中提供了此解决方案。

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

该值列表不能包含 * 或 ? 字符,而且值和 %VAR% 不应包含引号。如果 %VAR% 包含空格或特殊字符如 ^, & 等,则引号会导致问题。此解决方法的另一个限制是,除非添加指示变量,否则它不提供 ELSE 子句选项。优点是,它可以基于 IF /I 选项的存在或缺失来区分大小写。


很遗憾,由于文件匹配语义,对于包含问号的值(例如FOR %%A IN (-? -h -help)),将无法正常工作。 - Killroy
@Killroy - 是的,我已经注意到了答案中的限制。但是你的评论迫使我重新审视答案,并意识到使用FOR循环的解决方案还有其他限制。我稍微更新了答案的结尾部分。 - dbenham
使用echo和findstr不是一个选项吗? - Squashman
@Squashman - 嗯,是的,如果你想要遍历所有FINDSTR的错误和怪癖。 - dbenham
2
答案是错误的。Aacini所引用的命令失败了,但如果按以下方式进行编辑,则可以正常工作:for %%A in ("val1" "val2" "val3" ) do if "%VAR%"==%%A echo true - Ed999

67

我不这么认为。只需使用两个IF语句并跳转到同一个标签:

IF cond1 GOTO foundit
IF cond2 GOTO foundit

ECHO Didn't find it
GOTO end

:foundit
ECHO Found it!

:end

1
我正在学习PowerShell并调整我的所有脚本。但是我只需要修复一下批处理脚本中的小错误,想知道是否可能。当涉及到清洁代码时,我很挑剔lol。 - Anthony Miller
1
在许多情况下,调用(CALL)可能比跳转(GOTO)更可取,尽管这仅适用于您不需要ELSE子句的情况。 - Harry Johnston
@HarryJohnston 这取决于它是一个自上而下的程序/脚本还是结构化为批处理脚本中存在函数。在他的示例中,GOTO 是正确的,否则即使找到了也会出现“ECHO 没有找到”的情况。 - Anthony Miller
你应该在2019年真的使用PowerShell。 - zmbq
1
正确的英语应该是“没有找到它”。我本来想编辑这个问题,但发现已经有人做了,然后发现它被回滚了。 - Bigue Nique
显示剩余2条评论

14

使用简单的“FOR”循环可以在一行中使用“or”条件:

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

适用于您的情况:

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

避免重复执行

有评论指出可能会出现{execute_command}两次执行的情况。为了避免这种情况,您可以在第一次执行后使用goto命令。

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

如果你觉得有可能会执行两次 {execute_command}而且你不希望这样,你可以添加 && goto :eof

FOR %%a in (1 2) DO IF %var%==%%a ECHO TRUE && goto :eof

更简单了,而且仍然在一行上。


我发现这是批处理脚本中最直接和简单易用的情况。为什么它从未成为推荐答案? - myusrn
谢谢。我不知道,总的来说,在stackoverflow和其他论坛中,点赞是一个非常奇怪(而且可疑?)的过程。但在这种情况下,我想这是时机的问题。这个问题很旧,我的回答很新。无论如何,当我们能找到任何有效的答案时,我们都应该感到高兴。 :) - Apostolos
这种技术的一个可能缺点(尽管我喜欢这种方法的创新性!)是,根据条件,可能会执行两次命令,这可能不是想要的。 - TripeHound
理论上是可以的。但我已经创建了数百个批处理文件,每天都会使用几十个,从未遇到过这种情况。所以让我们保持实践! :) - Apostolos
今天我遇到了这样一个情况,我不想执行两次条件。如果文件a或文件b存在,我想要做一些事情。 - Simon Streicher
请告诉我昨天你遇到了什么问题。我很好奇! - Apostolos

10

谢谢您发布这篇文章,它对我很有帮助。

不知道这是否能帮助,但我也遇到了这个问题,由于您的帮助,我发现了另一种基于布尔等价性解决它的方法:

“A或B”的结果与“非(非A且非B)”相同。

因此:

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

变成:

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

但是这只提供了FALSE(ELSE)分支,如果两者都不为真。操作员希望如果任何一个为真,则提供TRUE分支。 - dbenham
@dbenham 那将是添加ELSE。答案很棒。 - Alex from Jitbit
@AlexfromJitbit - 是的,如果你能在这两个IF语句周围加上括号,然后执行一个ELSE,那么就会得到正确的答案。但是在批处理中没有简单的方法来做到这一点。这就是为什么通常会使用一个临时的辅助变量。 - dbenham

7
即使这个问题有点老:如果你想使用if cond1 or cond2,你不应该使用复杂的循环或类似的东西。简单地将两个if结合goto放在一起即可——这是一个隐式的或。
//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

如果没有使用goto但是采用“就地”操作,如果所有条件都匹配,则可能会执行该操作3次。


刚刚发现已经有人提供了这个答案。可能是我没注意到。 - dognose

4
在Batch中,没有IF <arg> ORELIFELSE IF语句,但是可以尝试将其他的IF语句嵌套在前一个IF的ELSE语句中。
IF <arg> (
    ....
) ELSE (
    IF <arg> (
        ......
    ) ELSE (
        IF <arg> (
            ....
        ) ELSE (
    )
)

3
这不是一个好的OR实现,因为每个ELSE IF都必须复制要执行的条件代码。(当然,如果条件代码是GOTO语句,那么你就有了一种更冗长的zmbq解决方案。) - dbenham

3
可以通过间接使用 IF 语句来实现该目标。
以下是一个复杂表达式的示例,可以在 CMD 批处理中相当简洁、合乎逻辑地编写,而不需要使用不连贯的标签和 GOTO。
() 括号之间的代码块由 CMD 处理为一种(可悲的)子 shell。无论代码块输出什么退出码,都将用于确定代码块在更大布尔表达式中的真/假值。使用这些代码块可以构建任意大的布尔表达式。
简单示例如下:
每个代码块被解析为真(即在代码块中执行最后一条语句后 ERRORLEVEL = 0)/假,直到整个表达式的值已经确定或控制跳出(例如通过 GOTO)。
 ((DIR c:\xsgdde /w) || (DIR c:\ /w)) && (ECHO -=BINGO=-)

复杂示例

这解决了最初提出的问题。每个块中可能有多个语句,但在 || || || 表达式中,尽可能简洁以使其易读性更强。^ 是CMD批处理中的转义字符,当放置在行末时,它将转义EOL并指示CMD继续在下一行上读取当前批处理语句。

@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

2

可以使用一个函数来评估OR逻辑并返回单个值。

@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,这个函数很适合使用ERRORLEVEL返回代码来返回结果。这样调用时就可以直接使用方便的&& ... || ...语法,而不需要额外的IF ... ELSE ...语句。 - dbenham

2

虽然没有OR运算符,但你可以写(伪代码)

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

喜欢

IF "%var%" == "1" SET "match=y"
IF "%var%" == "2" SET "match=y"
IF DEFINED match ECHO TRUE

请注意,如果var未定义,双引号可以防止触发语法错误。

1
If %x%==1 (
If %y%==1 (
:: both are equal to 1.
)
)

这是用于检查多个变量是否相等的。这是用于任一变量的。
If %x%==1 (
:: true
)
If %x%==0 (
If %y%==1 (
:: true
)
)
If %x%==0 (
If %y%==0 (
:: False
)
)

我脑海中突然想到了这个。我可以将其压缩得更小。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接