批处理文件中测试参数是否为空的正确方法是什么?

297

我需要测试一个变量是否被设置了。我尝试了几种技术,但当%1被引号包围时(例如%1"c:\some path with spaces"的情况)它们似乎都失败了。

IF NOT %1 GOTO MyLabel // This is invalid syntax
IF "%1" == "" GOTO MyLabel // Works unless %1 has double quotes which fatally kills bat execution
IF %1 == GOTO MyLabel // Gives an unexpected GOTO error.
根据这个网站的说法,以下是支持的IF语法类型。所以,我看不到有办法做到这一点。
IF [NOT] ERRORLEVEL number command
IF [NOT] string1==string2 command
IF [NOT] EXIST filename command

更新:在2020年10月25日,我更新了接受的答案,从使用方括号改为使用波浪号。大家都说波浪号更好,因为它更安全。我有些犹豫,因为波浪号看起来更复杂,其目的也不太明确,但尽管如此,我还是做出了改变。


2
在我的系统上(包括Windows 2003和Windows 7),只要%1中有偶数个双引号,if "%1" == "" GOTO MyLabel就不会致命地终止脚本的执行。我发现,如果%1中有奇数个双引号,则会出现以下错误并终止脚本的执行:The syntax of the command is incorrect. 下面的解决方案使用方括号来解决问题,被标记为正确答案,但似乎没有任何改善。当%1中有奇数个双引号时,该解决方案也会出现相同的错误。 - Susam Pal
一个很好的“万能”示例:https://dev59.com/AnRA5IYBdhLWcg3wyBD1#34552964,涵盖了文件/目录和通用字符串/数字混合参数。 - user257319
1
真令人沮丧——IF DEFINED只能用于环境变量而不能用于脚本变量,这浪费了很多潜力! - kayleeFrye_onDeck
我认为只有最后一个参数可以进行测试。如果您将中间参数留空,则shell会将后续参数移动以填充其位置。在此调用中:CMD.bat arg1, , arg3,以下内容是正确的:%2 == %arg3 - johny why
它可能会起作用,但在大多数情况下,您需要运行一个清理脚本来取消定义那些仅应为临时变量并限制于脚本的所有变量。IF /i NOT "%var%"==""是检查脚本变量是否已定义的几乎唯一安全方法。这比IF DEFINED %var%难看得多 :( - kayleeFrye_onDeck
显示剩余3条评论
18个回答

4

我通常使用这个:

IF "%1."=="." GOTO MyLabel

如果 %1 为空,IF 将比较 "." 和 ".",这将评估为 true。

4
这是错误的。这与以下内容相同:如果“%1”==“”,那么发表此帖子的原因是,如果%1本身带有引号,则会失败。 - blak3r
你是想测试 %1 是否为空还是是否有引号?问题不太清楚。如果在命令行上没有指定 %1,我的答案就有效。 - aphoria
你是在尝试测试%1是否为空还是是否带有引号?@aphoria,必须能够处理两者。 - Synetech
我用这个来判断%1%是否已经设置,对我很有帮助。谢谢,好答案! - Charlie A
1
aphoria - 抱歉十年后才回复,但我今天刚看到这个。为了扩展@blak3r所说的内容...例如,您的批处理文件命名为batch.cmd。简单来说,您执行:batch.cmd "。也就是说,只有一个参数,由一个双引号组成。或者batch.cmd ab"cd,或者任何更复杂的东西,通常被描述为具有奇数个["]标记。您的语句:IF "%1."=="." GOTO MyLabel将失败并导致批处理执行结束(崩溃),通常会出现错误:The syntax of the command is incorrect.,因为["]标记无法匹配。 - Kevin Fegan

4
总而言之:
set str=%~1
if not defined str ( echo Empty string )

如果%1为空字符串或空,则此代码将输出“空字符串”。将其添加到当前不正确的接受答案中。

3

脚本 1:

输入 ("Remove Quotes.cmd" "This is a Test")

该脚本的作用是删除指定文本中的引号。其中,第一个参数为脚本名称,第二个参数为需要处理的文本内容。
@ECHO OFF

REM Set "string" variable to "first" command line parameter
SET STRING=%1

REM Remove Quotes [Only Remove Quotes if NOT Null]
IF DEFINED STRING SET STRING=%STRING:"=%

REM IF %1 [or String] is NULL GOTO MyLabel
IF NOT DEFINED STRING GOTO MyLabel


REM   OR   IF "." equals "." GOTO MyLabel
IF "%STRING%." == "." GOTO MyLabel

REM GOTO End of File
GOTO :EOF

:MyLabel
ECHO Welcome!

PAUSE

输出(没有,%1不是空白、空的或NULL):


使用上述脚本1在没有任何参数的情况下运行“Remove Quotes.cmd”

输出(%1为空白、空的或NULL):

Welcome!

Press any key to continue . . .

注意:如果你在一个IF ( ) ELSE ( )语句中设置一个变量,直到它退出"IF"语句(除非启用了“延迟变量扩展”;一旦启用,请使用感叹号“!”代替百分号“%”符号),该变量将不可用于DEFINED。

例如:

脚本2:

Input ("Remove Quotes.cmd" "This is a Test")

@ECHO OFF

SETLOCAL EnableDelayedExpansion

SET STRING=%0
IF 1==1 (
  SET STRING=%1
  ECHO String in IF Statement='%STRING%'
  ECHO String in IF Statement [delayed expansion]='!STRING!'
) 

ECHO String out of IF Statement='%STRING%'

REM Remove Quotes [Only Remove Quotes if NOT Null]
IF DEFINED STRING SET STRING=%STRING:"=%

ECHO String without Quotes=%STRING% 

REM IF %1 is NULL GOTO MyLabel
IF NOT DEFINED STRING GOTO MyLabel

REM GOTO End of File
GOTO :EOF

:MyLabel
ECHO Welcome!

ENDLOCAL
PAUSE

输出:

C:\Users\Test>"C:\Users\Test\Documents\Batch Files\Remove Quotes.cmd" "This is a Test"  
String in IF Statement='"C:\Users\Test\Documents\Batch Files\Remove Quotes.cmd"'  
String in IF Statement [delayed expansion]='"This is a Test"'  
String out of IF Statement='"This is a Test"'  
String without Quotes=This is a Test  

C:\Users\Test>  

注意:它也会从字符串内部删除引号。
例如(使用脚本1或2): C:\ Users \ Test \ Documents \ Batch Files>“Remove Quotes.cmd”“This is”a“Test”
输出(脚本2):
String in IF Statement='"C:\Users\Test\Documents\Batch Files\Remove Quotes.cmd"'  
String in IF Statement [delayed expansion]='"This is "a" Test"'  
String out of IF Statement='"This is "a" Test"'  
String without Quotes=This is a Test  

在脚本2中执行("Remove Quotes.cmd"),不带任何参数:
输出:
Welcome!

Press any key to continue . . .

0

使用 ! 而不是 " 来检查空字符串

@echo off
SET a=
SET b=Hello
IF !%a%! == !! echo String a is empty
IF !%b%! == !! echo String b is empty

1
提示非常糟糕。引号至少可以处理空格和特殊字符,但感叹号不能。当启用延迟扩展时,感叹号变得非常棘手。 - jeb

0

我在网上找了很多答案,但是遇到了很多问题。大多数情况下都能解决,但总有一些特殊情况会导致它们失效。
也许如果有引号就无法工作,也许如果没有引号就会出错,如果变量有空格就会语法错误,有些只适用于参数(而不是环境变量),其他技术允许一个空的引号集合通过“定义”,而一些更棘手的则不允许您在之后链接一个else

这里有一个解决方案,我对它感到满意,请告诉我如果你发现有任何特殊情况它不能解决。

:ifSet
if "%~1"=="" (Exit /B 1) else (Exit /B 0)

将该子程序放在您的脚本中或者它自己的.bat文件中,都应该可以工作。
因此,如果您想编写以下伪代码:

if (var)
then something
else somethingElse

你可以写:

(Call :ifSet %var% && (
    Echo something
)) || (
    Echo something else
)

它在我所有的测试中都有效:

(Call :ifSet && ECHO y) || ECHO n
(Call :ifSet a && ECHO y) || ECHO n
(Call :ifSet "" && ECHO y) || ECHO n
(Call :ifSet "a" && ECHO y) || ECHO n
(Call :ifSet "a a" && ECHO y) || ECHO n

输出 n, y, n, y, y


更多示例:
  • 只想检查 if 吗? Call :ifSet %var% && Echo set
  • 只是如果不是(只有 else)吗? Call :ifSet %var% || Echo set
  • 检查传递的参数;运行良好。 Call :ifSet %1 && Echo set
  • 不想在脚本中堆积代码,所以将其放在自己的 ifSet.bat 中?没问题:((Call ifSet.bat %var%) && Echo set) || (Echo not set)

发现一个问题;如果您尝试使用if else语法,并且then块中的最后一条命令失败,则else将执行:(Call ifSet.bat YES && (Echo true & Echo x | findstr y)) || Echo,输出true false - Hashbrown
实际上,如果这是一个问题,并且您可以处理额外的行,您可以通过单独调用ifSet来减轻这个问题,然后使用If %errorlevel% EQU 0 (x) Else (y) - Hashbrown
接受的答案(使用方括号)不适用于哪些情况?您能解释一下 Exit /B 是什么意思吗? - blak3r
由于某种原因,当我使用“set”变量(例如“[%bob%]”,仅适用于像“[%2]”这样的参数)进行测试时,它会出现错误,但是现在重新检查它可以正常工作。我想我现在唯一不满意的是“[]”允许空引号通过,而这种情况则不行(所以这取决于您的用例)。 - Hashbrown
/B 标志阻止整个批处理退出,仅退出子例程或 call 的批处理。除非您想知道整个命令在执行什么(http://ss64.com/nt/exit.html);它返回一个错误代码以让调用脚本知道子例程的结果(`0` 通过,1 失败),可通过答案中的布尔逻辑或上面评论中的 %errorlevel% 使用。 - Hashbrown

0

最简单的解决方案,只需两行代码:

SET /A var02 = %var01% / 1
IF %ERRORLEVEL% NEQ 0 (ECHO Variable var01 is NOT EXIST)

1
简单但是错误的。使用set "var01=User&Doc"set var01=---或者set var01=1 0 0进行测试。 - jeb
你说得对。我的例子只是检查变量是否存在,而不是它是否为空。我误解了主题任务。 - alive-one

0
This way looks correct:

if "%~1" == "" if [%1] == [] echo Argument 1 is really empty.

First filter (if "%~1" == "") из safe way to detect argument is empty or "".
Second filter (if [%1] == []) will skip "" argument.
Remember we have two kind of empty arguments : really empty (nothing) and empty quotes "".
We have to detect both.

Next code takes all arguments "as is" in variable args:

:GetArgs
set args=%1
:ParseArgs
shift /1
if "%~1" == "" if [%1] == [] goto :DoneArgs
set args=%args% %1
goto :ParseArgs
:DoneArgs
if not defined args echo No arguments
if defined args echo Aguments are: %args%

This code correctly process "normal" arguments (simple words, "multiword phrases" or "") with balanced quotes.
Quoted arguments can even contain special chars like "a & b".
But it is still can fail with "abnormal" expressions with unbalanced quotes (do not use them).

1
但是对于 myBatch ^&,它会出现语法错误。顺便说一下,使用 [] 是无用的,它们没有特殊含义,也不能转义任何内容。 - jeb

-1
我在不到一个月的时间里就进来了(尽管这是8年前的问题)...我希望他/她现在已经超越了批处理文件。;-) 我以前经常这样做。但是,我不确定最终目标是什么。如果他/她像我一样懒,我的go.bat可以用于这种情况。(见下文) 但是,如果您直接使用输入作为命令,则OP中的命令可能无效。即,

"C:/Users/Me"

是无效的命令(或者如果你在另一个驱动器上,曾经是无效的)。你需要将其分成两个部分。

C:
cd /Users/Me

还有,第二个问题是什么意思?“定义”或“未定义”是什么意思?GIGO。我使用默认值来捕获错误。如果输入没有被捕获,它会转到帮助(或默认命令)。因此,没有输入不是错误。您可以尝试将cd输入并捕获错误(如果有错误)。 (好的,使用go“下载(只有一个括号)被DOS捕获。(严厉!))

cd "%1"
if %errorlevel% neq 0 goto :error

而且,在路径周围只需要使用3个引号,而不是命令。例如,

"cd C:\Users" 

在旧时代,除非你在不同的驱动器上,否则情况很糟糕。

cd "\Users" 

是功能性的。

cd "\Users\Dr Whos infinite storage space"

如果您的路径中有空格,则此方法可行。

@REM go.bat
@REM The @ sigh prevents echo on the current command
@REM The echo on/off turns on/off the echo command. Turn on for debugging
@REM You can't see this.
@echo off
if "help" == "%1" goto :help

if "c" == "%1" C:
if "c" == "%1" goto :done

if "d" == "%1" D:
if "d" == "%1" goto :done

if "home"=="%1" %homedrive%
if "home"=="%1" cd %homepath%
if "home"=="%1" if %errorlevel% neq 0 goto :error
if "home"=="%1" goto :done

if "docs" == "%1" goto :docs

@REM goto :help
echo Default command
cd %1
if %errorlevel% neq 0 goto :error
goto :done

:help
echo "Type go and a code for a location/directory
echo For example
echo go D
echo will change disks (D:)
echo go home
echo will change directories to the users home directory (%homepath%)
echo go pictures
echo will change directories to %homepath%\pictures
echo Notes
echo @ sigh prevents echo on the current command
echo The echo on/off turns on/off the echo command. Turn on for debugging
echo Paths (only) with folder names with spaces need to be inclosed in         quotes (not the ommand)
goto :done

:docs
echo executing "%homedrive%%homepath%\Documents"
%homedrive%
cd "%homepath%\Documents"\test error\
if %errorlevel% neq 0 goto :error
goto :done

:error
echo Error: Input (%1 %2 %3 %4 %5 %6 %7 %8 %9) or command is invalid
echo go help for help
goto :done

:done

cd 命令有一个 /d 开关的原因是: cd /d "C:\Users\Me" (对于 Windows 来说,正确的路径分隔符是 \,而不是 /)。 - Stephan
这是一篇很长的帖子,但并没有试图回答问题。它是关于如何检测空参数,而不是“空参数的默认行为可能是什么”的内容。 - jeb

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