DOS批处理中的逻辑运算符("and"、"or")

312

你如何在DOS批处理文件中实现逻辑运算符?


7
我为您的观赏愉悦提交:pi.bat :) http://thedailywtf.com/Articles/Stupid-Coding-Tricks-A-Batch-of-Pi.aspx - Scott Arrington
14个回答

372

你可以使用嵌套条件进行 and 操作:

if %age% geq 2 (
    if %age% leq 12 (
        set class=child
    )
)

你也可以使用简写形式:

if %age% geq 2 if %age% leq 12 set class=child

您可以使用单��的变量来执行or操作:

set res=F
if %hour% leq 6 set res=T
if %hour% geq 22 set res=T
if "%res%"=="T" (
    set state=asleep
)

请注意,本答案针对的是Windows中找到的cmd批处理语言。您提到了“DOS批处理”,但基于几个要点,我认为前者是一个安全的选择(1)
如果您真的指的是原始的MS-DOS批处理语言,您应该记住if语句要简单得多,您可能需要使用一些if ... goto块来控制流程,而不是(例如)括号或else

(1)支持以下观点:

  • 存在cmdwindows-console标签;
  • 一些人之前的经验是无法识别cmd和MS-DOS批处理语言之间的实际差异,并将DOC与cmd终端窗口混淆;
  • 问题使用更通用的“DOS”而不是具体的“MS-DOS”(其中“DOS”可能是任何磁盘操作系统);
  • 这是Stack Overflow而不是复古计算机姊妹网站,在那里提问关于MS-DOS的问题更加合适(我也经常访问那个网站,对于我们记得和欣赏计算机历史的人来说很好);和
  • 原始提问者(最终)接受答案,表明解决方案起作用。

18
你也可以使用 set res=set res=1 然后使用 if defined res,这样更能避免拼写错误,并且即使在没有显式启用延迟扩展的代码块中也能正常工作。 - Joey
7
稍微改进一下你的回答...你不需要显式地嵌套"if"语句...你可以像Dave Jarvis以下示范的那样只是"链接"它们。 - JoelFan
请注意,在set=true之后不要留下任何空格,否则程序将无法正常工作。它会在变量中存储值"true"。 - Roland Pihlakas
1
您所描述的“或”条件对于一系列数字非常有效,但如果我要比较两个独立的值而不是一个范围怎么办?例如,如果此文件存在或文件名==""。 - bakoyaro
1
@bakoyaro,你可以使用任何条件将res设置为true,包括你提到的那些条件。 - paxdiablo
显示剩余2条评论

89

IF语句不支持逻辑运算符ANDOR。级联的IF语句隐含了一个连接词:

IF Exist File1.Dat IF Exist File2.Dat GOTO FILE12_EXIST_LABEL
如果 File1.DatFile2.Dat 存在,则跳转到标签 FILE12_EXIST_LABEL
另请参阅:IF /?

66

德摩根定理允许我们使用合取("AND")和否定("NOT")将析取("OR")转换成逻辑等价式。这意味着我们可以将多个析取("OR")串联在一行上。

这意味着如果名称是"Yakko"、"Wakko"或"Dot",那么输出"Warner兄弟或姐妹"。

set warner=true
if not "%name%"=="Yakko" if not "%name%"=="Wakko" if not "%name%"=="Dot" set warner=false
if "%warner%"=="true" echo Warner brother or sister

这是paxdiablo "OR"示例的另一个版本,但条件被链接在一行上。(请注意,leq的反义词是gtr,而geq的反义词是lss。)

set res=true
if %hour% gtr 6 if %hour% lss 22 set res=false
if "%res%"=="true" set state=asleep

8

OR运算符有点棘手,但并不是太难。以下是一个例子:

set var1=%~1
set var2=%~2
::
set or_=
if "%var1%"=="Stack" set or_=true
if "%var2%"=="Overflow" set or_=true
if defined or_ echo Stack OR Overflow

不起作用。如果我给它 Stack,它会回显。如果我给它 Overflow,它就不会 - 但它应该会。 - Unknow0059
1
@Unknow0059:如果第一个参数是Stack或第二个参数是Overflow,它会回显。我怀疑这可能是Timo的错误(应该只使用var1),但这并不意味着这个概念行不通。 - paxdiablo

6
以下示例展示了如何创建一个AND语句(用于设置变量或包含命令参数)。
打开记事本并关闭CMD窗口的命令如下:
start notepad.exe & exit

如果变量'a'等于blah,则将变量x、y和z设置为相应的值。

IF "%a%"=="blah" (set x=1) & (set y=2) & (set z=3)

希望这能有所帮助!

3
如果您有兴趣在一个语句中编写if+AND/OR,那么这是不可能的。但是,您仍然可以使用&&/||(/)语句来分组if,以实现您想要的结果,而无需添加任何额外的变量和if-else块重复(单个echo命令用于TRUEFALSE代码部分)。
@echo off
    
setlocal
    
set "A=1" & set "B=2" & call :IF_AND
set "A=1" & set "B=3" & call :IF_AND
set "A=2" & set "B=2" & call :IF_AND
set "A=2" & set "B=3" & call :IF_AND
    
echo.
    
set "A=1" & set "B=2" & call :IF_OR
set "A=1" & set "B=3" & call :IF_OR
set "A=2" & set "B=2" & call :IF_OR
set "A=2" & set "B=3" & call :IF_OR
    
exit /b 0
    
:IF_OR
( ( if %A% EQU 1 ( type nul>nul ) else type 2>nul ) || ( if %B% EQU 2 ( type nul>nul ) else type 2>nul ) || ( echo.FALSE-& type 2>nul ) ) && echo TRUE+
    
exit /b 0
    
:IF_AND
( ( if %A% EQU 1 ( type nul>nul ) else type 2>nul ) && ( if %B% EQU 2 ( type nul>nul ) else type 2>nul ) && echo.TRUE+ ) || echo.FALSE-

    
exit /b 0

更统一的版本:

@echo off

setlocal

set "A=1" & set "B=2" & ( call :IF_AND "%%A%% EQU 1" "%%B%% EQU 2" && echo TRUE+ ) || echo FALSE-
set "A=1" & set "B=3" & ( call :IF_AND "%%A%% EQU 1" "%%B%% EQU 2" && echo TRUE+ ) || echo FALSE-
set "A=2" & set "B=2" & ( call :IF_AND "%%A%% EQU 1" "%%B%% EQU 2" && echo TRUE+ ) || echo FALSE-
set "A=2" & set "B=3" & ( call :IF_AND "%%A%% EQU 1" "%%B%% EQU 2" && echo TRUE+ ) || echo FALSE-

echo.

set "A=1" & set "B=2" & ( call :IF_OR "%%A%% EQU 1" "%%B%% EQU 2" && echo TRUE+ ) || echo FALSE-
set "A=1" & set "B=3" & ( call :IF_OR "%%A%% EQU 1" "%%B%% EQU 2" && echo TRUE+ ) || echo FALSE-
set "A=2" & set "B=2" & ( call :IF_OR "%%A%% EQU 1" "%%B%% EQU 2" && echo TRUE+ ) || echo FALSE-
set "A=2" & set "B=3" & ( call :IF_OR "%%A%% EQU 1" "%%B%% EQU 2" && echo TRUE+ ) || echo FALSE-

exit /b 0

:IF_OR
if %~1 ( exit /b 0 ) else if %~2 ( exit /b 0 ) else exit /b 1
exit /b -1

:IF_AND
if %~1 ( if %~2 ( exit /b 0 ) else exit /b 1 ) else exit /b 1
exit /b -1

输出:

TRUE+
FALSE-
FALSE-
FALSE-
    
TRUE+
TRUE+
TRUE+
FALSE-

技巧在于type命令,它会丢弃/设置errorlevel并处理到下一个命令的方式。

这里实际上检查的条件是什么?很难说。 - Unknow0059
1
@Unknow0059 if %A% EQU 1if %B% EQU 2,其中 :IF_OR 分别代表 if %A% EQU 1if %B% EQU 2 - Andry

2
Athul Prakash(当时年仅16岁)提出了一种实现OR测试的逻辑思路,即通过否定IF语句中的条件,然后使用ELSE子句作为需要执行的代码位置。我想到他建议使用两个IF语句,通常需要两个ELSE子句,因此需要编写两次执行代码。但是,如果使用GOTO跳过所需代码,而不是编写ELSE子句,则只需要编写一次执行代码。
以下是一个可测试的示例,展示了如何使用Athul Prakash的负逻辑来创建OR
在我的示例中,只要有坦克驾驶执照或正在服役,就可以允许某人驾驶坦克。在两个提示处输入truefalse,您将能够看到逻辑是否允许您驾驶坦克。
@ECHO OFF
@SET /p tanklicence=tanklicence:
@SET /p militaryservice=militaryservice:

IF /I NOT %tanklicence%==true IF /I NOT %militaryservice%==true GOTO done

ECHO I am driving a tank with tanklicence set to %tanklicence% and militaryservice set to %militaryservice%

:done

PAUSE

2

以下是简单易懂的操作步骤:

AND> if+if

(注:保留原文中的HTML标签)
if "%VAR1%"=="VALUE" if "%VAR2%"=="VALUE" *do something*

OR> if // if

set BOTH=0
if "%VAR1%"=="VALUE" if "%VAR2%"=="VALUE" set BOTH=1
if "%BOTH%"=="0" if "%VAR1%"=="VALUE" *do something*
if "%BOTH%"=="0" if "%VAR2%"=="VALUE" *do something*

我知道还有其他答案,但我认为我的更简单,因此更容易理解。希望这能帮到你!;)

2
很多人似乎忽略了OR的最明显解决方案,那就是使用标签。
if "%a%" == "ONE" goto do_thing
if "%a%" == "TWO" (
   :do_thing
   echo a is equal to ONE or TWO
)

1

只有 OR 部分比较棘手,但是对于包含 NOT OR AND 的一般布尔表达式,唯一好的解决方案就是这个:

REM if A == B OR C == C then yes

(call :strequ A B || call :strequ C C) && echo yes
exit /b

:strequ
if "%1" == "%2" exit /b 0
exit /b 1

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