如果...或如果...在 Windows 批处理文件中
Posted
技术标签:
【中文标题】如果...或如果...在 Windows 批处理文件中【英文标题】:IF... OR IF... in a windows batch file 【发布时间】:2012-01-16 08:00:14 【问题描述】:有没有办法在 Windows 批处理文件中编写 IF OR IF 条件语句?
例如:
IF [%var%] == [1] OR IF [%var%] == [2] ECHO TRUE
【问题讨论】:
没有。可以直接写一个 AND:IF [%var%]==[1] IF [%var2%]==[2] ECHO BOTH ARE TRUE
。我以前定义SET AND=IF
,然后写:IF cond1 %AND% cond2 ECHO BOTH ARE TRUE
。
这能回答你的问题吗? Logical operators ("and", "or") in DOS batch
使用 SET AND=IF 的好技巧 :-) 但是不要在这种伪逻辑操作之后尝试使用 ELSE :-)
【参考方案1】:
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 逻辑,因为如果第一个条件为真,它的执行速度可能会快一点,但我从不打扰。
附录 - 这是一个重复的问题,与Using an OR in an IF statement WinXP Batch Script的答案几乎相同
最终附录 - 我几乎忘记了我最喜欢的测试变量是否是不区分大小写值列表中的任何一个的技术。初始化包含可接受值的分隔列表的测试变量,然后使用搜索和替换来测试您的变量是否在列表中。这非常快,并且对任意长的列表使用最少的代码。它确实需要延迟扩展(或者 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=" 这样的 FOR 选项
通过添加更多代码,即使在 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 不能处理包含问号的值,例如 FOR %%A IN (-? -h -help)。 @Killroy - 是的,我已经注意到答案中的限制。但是您的评论迫使我查看答案并意识到 FOR 解决方案还有其他限制。我已经更新了答案的结尾。 回声到 findstr 不是一个选项吗? @Squashman - 嗯,是的,如果你想浏览所有 FINDSTR 错误和怪癖。 答案是错误的。归因于 Aacini 的命令按引用失败,但如果按如下方式编辑则有效:for %%A in ("val1" "val2" "val3" ) do if "%VAR%"==%%A echo true【参考方案2】:我不这么认为。只需使用两个 IF 和 GOTO 相同的标签:
IF cond1 GOTO foundit
IF cond2 GOTO foundit
ECHO Didn't found it
GOTO end
:foundit
ECHO Found it!
:end
【讨论】:
是的,据我所知,这两个是唯一的选择。 =D 我实际上正在学习 powershell 并调整我的所有脚本。但只需要对批处理脚本进行小错误修复,并想知道这是否可能。在清洁代码方面我很挑剔lol 在许多情况下,CALL 可能比 GOTO 更可取,尽管这只适用于不需要 ELSE 子句的情况。 @HarryJohnston 这取决于它是否只是一个自上而下的程序/脚本,或者它的结构是否像批处理脚本中存在的函数一样运行=/。在他的示例中,GOTO 是正确的,否则即使找到ECHO Didn't find it
,您也会点击它。
正确的英文应该是“没找到”。我正要编辑问题,但确实发现有人已经这样做了,然后发现它被回滚了。【参考方案3】:
可以在一行中使用简单的“FOR”来使用“或”条件:
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
简单得多,而且仍然在一行中。
【讨论】:
我发现这是我的批处理脚本中最直接和最简单的用例。为什么这从来没有成为推荐的答案? 谢谢。我不知道,一般来说,upvoting 是一个非常奇怪的过程(而且很可疑?),不仅在 *** 中,而且在其他论坛中也是如此。但在这种情况下,我想是时候了。这个问题很老,我的回答是最近的。无论如何,当我们能找到 any 有效的答案时,我们一定会很高兴。 :) 这种技术的一个可能缺点(虽然我喜欢这种方法的开箱即用!)是根据条件,可能执行命令两次,这可能是不想要的。 理论上是的。但是我已经创建了数百个批处理文件,我每天使用几十个,我从来没有遇到过这样的案例。所以让我们继续练习吧! :) 我今天有这样一个案例,不想执行两次条件。如果文件 a 或文件 b 存在,我想做点什么。【参考方案4】:感谢这篇文章,它对我帮助很大。
不知道它是否可以提供帮助,但我遇到了这个问题,多亏了你,我发现了我认为基于此布尔等价的另一种解决方法:
“A or B”与“not(not A and not B)”相同
因此:
IF [%var%] == [1] OR IF [%var%] == [2] ECHO TRUE
变成:
IF not [%var%] == [1] IF not [%var%] == [2] ECHO FALSE
【讨论】:
但这仅在两者都不为真时提供 FALSE (ELSE) 分支。如果其中一个为真,则 OP 想要 TRUE 分支。 @dbenham 将添加 ELSE。答案很好 @AlexfromJitbit - 是的,如果你可以在两个 IF 语句周围加上括号,然后执行 ELSE,那将给出正确的答案。但是没有简单的方法可以批量执行此操作。这就是为什么经常使用临时辅助变量的原因。【参考方案5】:即使这个问题有点老了:
如果你想使用if cond1 or cond 2
- 你不应该使用复杂的循环或类似的东西。
简单地同时提供ifs
和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 而是一个“inplace”操作,如果所有条件都匹配,您可能会执行该操作 3 次。
【讨论】:
刚刚发现这个答案已经提供。必须监督。【参考方案6】:Batch 中没有 IF <arg> OR
或 ELIF
或 ELSE IF
,但是...
尝试将其他 IF 嵌套在前一个 IF 的 ELSE 中。
IF <arg> (
....
) ELSE (
IF <arg> (
......
) ELSE (
IF <arg> (
....
) ELSE (
)
)
【讨论】:
这不是 OR 的良好实现,因为必须为每个 ELSE IF 复制要执行的条件代码。 (当然,除非条件代码是 GOTO,在这种情况下,您有更详细的 zmbq 解决方案形式。)【参考方案7】:可以使用一个函数,它计算 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 ...
语句。【参考方案8】:
可以通过间接使用 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
【讨论】:
【参考方案9】: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
)
)
我只是想到了这一点。我可以更紧凑。
【讨论】:
【参考方案10】:我意识到这个问题已经过时了,但我想发布一个替代解决方案,以防其他人(比如我自己)在遇到相同问题时发现这个帖子。通过回显变量并使用 findstr 进行验证,我能够解决缺少 OR 运算符的问题。
for /f %%v in ('echo %var% ^| findstr /x /c:"1" /c:"2"') do (
if %errorlevel% equ 0 echo true
)
【讨论】:
【参考方案11】:虽然 dbenham 的回答非常好,但如果您检查的变量不是 environment 变量,则依赖 IF DEFINED
会给您带来很多麻烦。脚本变量没有得到这种特殊处理。
虽然这看起来像是一些可笑的无证 BS,但对 IF
和 IF /?
进行简单的 shell 查询会发现,
DEFINED 条件的工作方式与 EXIST 类似,只是它需要一个 environment 变量名称,如果 environment 变量返回 true 已定义。
关于回答这个问题,是否有理由在经过一系列评估后不只使用一个简单的标志?就底层逻辑和可读性而言,这对我来说似乎是最灵活的OR
检查。例如:
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)
显然,它们可以是任何类型的条件评估,但我只是分享一些示例。
如果您想以书面形式将所有内容放在一行上,您可以将它们与&&
链接在一起,例如:
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)
【讨论】:
【参考方案12】:从来没有存在过工作。
我用 如果不存在 g:xyz/what goto h: 否则 xcopy c:current/files g:bu/current 有修饰符 /a 等。不确定是哪些。店里的笔记本电脑。和办公室里的电脑。我不在。
从来没有让批处理文件在 Windows XP 上运行
【讨论】:
如您的示例中所写,“如果不存在 g:xyz/what”,xyz/what 相对于 g: 驱动器的当前目录,可能不是 g 的根文件夹。你在这里需要“g:\xyz\what”吗?与 c:current/files 和 g:bu/current 相同。请注意,每个驱动器都有不同的当前文件夹,即使该驱动器不是您当前的工作目录,它也会被“记住”。【参考方案13】:我通常使用的一个更快的替代方案如下,因为我可以“或”任意数量的条件来适应可变空间
@(
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.
)
【讨论】:
【参考方案14】:很简单,下面用就行了
IF %var% == 1 (
ECHO TRUE)
IF %var% == 2 (
ECHO TRUE)
【讨论】:
【参考方案15】:另一个选项是显示当前环境变量并利用FINDSTR
的默认行为:
FINDSTR "hello there" x.y
在文件 x.y 中搜索“hello”或“there”。
所以
SET | FINDSTR /I /X "var=1 var=2" >NUL
IF %ERRORLEVEL% EQU 0 (
ECHO TRUE
) ELSE (
ECHO FALSE
)
在哪里
/I Specifies that the search is not to be case-sensitive. /X Prints lines that match exactly.
如果首选正则表达式,请改用FINDSTR /R /I "^var=1$ ^var=2$" >NUL
。
编辑:如果变量字符串包含空格,则应使用FINDSTR /R
,例如FINDSTR /R /I "^var=1 a$ ^var=2 b$" >NUL
。
编辑:如果变量字符串包含空格,则应使用文字搜索字符串。例如,FINDSTR /I /X /C:"var=1 a" /C:"var=2 b" >NUL
。
【讨论】:
您可以使这个答案更有效,只需将SET
更改为SET var
,或者最好将(SET var) 2>NUL
。要执行问题中显示的任务,作为单行,您只需使用条件执行,即(Set var) 2>NUL | %SystemRoot%\System32\findstr.exe /B /E /I "var=1 var=2" 1>NUL && Echo TRUE
。
@Compo 使用不带参数的SET
允许使用不同的变量,例如my_test_var
和your_test_var
。
您的变量和 OP 的变量被专门命名为 var
。在这种情况下列出每个变量是绝对没有必要的。因此效率低下。至少使用var
只会返回名称以这三个字符开头的那些!【参考方案16】:
意识到这是一个老问题,回复帮助我想出了一个解决方案来测试批处理文件的命令行参数;所以我也想发布我的解决方案,以防其他人正在寻找类似的解决方案。
首先我要指出的是,我无法让 IF ... ELSE 语句在 FOR ... DO 子句中工作。事实证明(感谢 dbenham 在他的示例中无意中指出了这一点)ELSE 语句不能与右括号分开。
所以不要这样:
FOR ... DO (
IF ... (
)
ELSE (
)
)
出于可读性和美学原因,这是我的偏好,您必须这样做:
FOR ... DO (
IF ... (
) ELSE (
)
)
现在 ELSE 语句不会作为无法识别的命令返回。
最后,这就是我试图做的——我希望能够以任何顺序将多个参数传递给批处理文件,忽略大小写,并报告/失败传入的未定义参数。所以这是我的解决方案...
@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
注意,这只会报告第一个失败的参数。因此,如果用户传递了多个不可接受的参数,他们只会被告知第一个直到它被纠正,然后是第二个,等等。
【讨论】:
-1 这个问题与“FOR 循环中的 IF 语法”无关,也与如何正确传递参数无关,而是与发现复制 If.. Or.. If 的方法有关批处理文件中的语句。 很公平。猜猜我是在回答我自己的问题,这些答案对我有帮助,而不是仔细回答 OP 的原始问题。以后会更加小心。谢谢机甲闪! -R 如果您有类似的问题,并且已经自己解决了该问题,并且在 *** 上没有发布与您相同的问题,您可以将其作为问题提出并检查框自己回答。这样它就会出现在网站上,人们也可以对其做出回应。以上是关于如果...或如果...在 Windows 批处理文件中的主要内容,如果未能解决你的问题,请参考以下文章