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

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个回答

336

使用方括号而不是引号:

IF [%1] == [] GOTO MyLabel

圆括号是不安全的:只使用方括号。


14
括号不安全! 像 %1 中的 & 这样的内容会打破行。 - jeb
20
如果参数中没有空格,那么像“if #%1#==##”或“if [%1]==[]”这样使用其他字符是可行的。否则,你将会得到一个“...在此时意外”的错误。 - Synetech
20
如我在回答中所提到的,“%~1”真的是更好的方法。请使用它。 - jamesdlin
11
伙计们,@jamesdlin是正确的。如果变量text中有空格或等于号==,这种方法将无效。请改用他的答案。 - James Ko
9
你没有明确说明“它不支持空格”。请使用@jamesdlin的答案。 - JB.
显示剩余7条评论

237

你可以使用:

IF "%~1" == "" GOTO MyLabel

去掉外层引号。一般来说,这比使用方括号更可靠,因为即使变量中有空格,它也可以正常工作。


12
这确实是一种好的方法。使用 ~,如果外部有引号,则去掉引号,然后手动添加回来(确保只有一对)。此外,如果参数包含空格,你必须使用引号(我之前使用 # 或括号而不是引号的版本会导致带有空格的参数抛出 ...was unexpected at this time 错误,我曾经吃过这个亏)。 - Synetech
6
这也适用于像 %MYVAR% 这样的普通变量吗?而不仅仅是像 %1 这样的参数? - simpleuser
6
抱歉,您是正确的,它不能与一般环境变量一起使用。 - jamesdlin
2
注意字符串中使用双引号 '"' 作为转义序列时会导致脚本崩溃,因为在尝试比较时会出错。例如:""这个字符串将会导致比较崩溃""。双引号 '"' 是正确的转义序列,如果字符串为空,则对双引号进行替换将失败。 - Tydaeus
2
@Amalgovinus 你可以将变量作为参数传递给子程序,让子程序使用"%~1" - jamesdlin
显示剩余4条评论

49

最好的半解决方案之一是将%1复制到一个变量中,然后使用延迟扩展,因为延迟扩展对任何内容都是安全的。

set "param1=%~1"
setlocal EnableDelayedExpansion
if "!param1!"=="" ( echo it is empty )
rem ... or use the DEFINED keyword now
if defined param1 echo There is something

这样做的好处是处理param1绝对安全。
而且设置param1在许多情况下都有效,比如:
test.bat hello"this is"a"test
test.bat you^&me

但它仍然失败并显示奇怪的内容,如:
test.bat ^&"&

为了能够得到100%正确的答案,需要

它检测%1是否为空,但对于某些内容,它无法获取内容。
这也可以用于区分空的%1和带有""%1
它利用了CALL命令在不中止批处理文件的情况下失败的能力。

@echo off
setlocal EnableDelayedExpansion
set "arg1="
call set "arg1=%%1"

if defined arg1 goto :arg_exists

set "arg1=#"
call set "arg1=%%1"
if "!arg1!" EQU "#" (
    echo arg1 exists, but can't assigned to a variable
    REM Try to fetch it a second time without quotes
    (call set arg1=%%1)
    goto :arg_exists
)

echo arg1 is missing
exit /b

:arg_exists
echo arg1 exists, perhaps the content is '!arg1!'

如果你想要完全防弹地获取内容,你可以阅读如何接收最奇怪的命令行参数?


21

来自IF /?:

如果启用了命令扩展,IF 将会作如下更改:

IF [/I] string1 compare-op string2 command
IF CMDEXTVERSION number command
IF DEFINED variable command

DEFINED条件与EXISTS非常相似,只不过它接受一个环境变量名称作为参数,并在环境变量被定义时返回true。


2
是的,我实际上尝试了这种方法,据我所知,这仅适用于环境变量。因此,无法使用 %1 或在批处理文件内定义的变量。 - blak3r
2
这只适用于%1和类似的情况。实际上,“批处理文件内定义的变量”在批处理文件运行时环境变量,您可以在它们上面使用IF DEFINED - zb226

19

很遗憾,我没有足够的声望来评论或投票目前的答案,所以我不得不自己写。

最初问题中写的是“变量”,而不是“参数”,这让人非常困惑,特别是因为这是谷歌搜索如何测试空变量的第一条链接。由于Stephan已经编辑了原始问题并使用了正确的术语,但我决定保留我的回答以帮助消除任何混淆,特别是以防谷歌仍然将人们引导到这里寻找变量:

%1 不是变量!它是一个命令行参数。

非常重要的区别。单个百分号后跟一个数字表示一个命令行参数,而不是变量。

使用set命令设置变量,并使用两个百分号(一个在前面,一个在后面)来召回变量。例如 %myvar%

要测试空变量,您可以使用“if not defined”语法(专门为变量编写的命令不需要任何百分号),例如:

set myvar1=foo  
if not defined myvar1 echo You won't see this because %myvar1% is defined.  
if not defined myvar2 echo You will see this because %myvar2% isn't defined.

(如果您想测试命令行参数,我建议参考jamesdlin的答案。)

1
你是对的。但正确的方式应该只是将标题从“if variable is empty”更正为“if parameter is empty”。我已经这样做了。(冒着使一些检查变量而不是参数的答案无效的风险,因为它们没有回答问题而是无效的) - Stephan
1
好的,谢谢Stephan,我没意识到你可以这样做。我已经编辑了我的答案以反映更新。 - AutoMattTick

7
您可以使用

标签来创建段落。

if defined (variable) echo That's defined!
if not defined (variable) echo Nope. Undefined.

1
欢迎来到SO!你的回答似乎是这个问题的好答案。一旦你有足够的声望,你就可以在任何帖子上评论。还要检查一下这个我能做什么代替 - thewaywewere

6
请使用“IF DEFINED variable command”来测试批处理文件中的变量。
但如果您想要测试批处理参数,请尝试以下代码,以避免棘手的输入(如“1 2”或ab ^ > cd)。
set tmp="%1"
if "%tmp:"=.%"==".." (
    echo empty
) else (
    echo not empty
)

5

我根据这里的答案创建了这个小批处理脚本,因为有很多有效的答案。只要按照相同的格式添加就可以了。

REM Parameter-testing

Setlocal EnableDelayedExpansion EnableExtensions

IF NOT "%~1"=="" (echo Percent Tilde 1 failed with quotes) ELSE (echo SUCCESS)
IF NOT [%~1]==[] (echo Percent Tilde 1 failed with brackets) ELSE (echo SUCCESS)
IF NOT  "%1"=="" (echo Quotes one failed) ELSE (echo SUCCESS)
IF NOT [%1]==[] (echo Brackets one failed) ELSE (echo SUCCESS)
IF NOT "%1."=="." (echo Appended dot quotes one failed) ELSE (echo SUCCESS)
IF NOT [%1.]==[.] (echo Appended dot brackets one failed) ELSE (echo SUCCESS)

pause

5

我用以下代码进行测试,结果很好。

@echo off

set varEmpty=
if not "%varEmpty%"=="" (
    echo varEmpty is not empty
) else (
    echo varEmpty is empty
)
set varNotEmpty=hasValue
if not "%varNotEmpty%"=="" (
    echo varNotEmpty is not empty
) else (
    echo varNotEmpty is empty
)

你的解决方案在变量包含引号时会失败。顺便说一下,存在语法IF DEFINED var是有原因的。 - jeb

4

空字符串是一对双引号/"",我们可以通过测试长度来进行验证:

set ARG=%1
if not defined ARG goto nomore

set CHAR=%ARG:~2,1%
if defined CHAR goto goon

接着测试它的2个字符是否与双引号匹配:

if ^%ARG:~1,1% == ^" if ^%ARG:~0,1% == ^" goto blank
::else
goto goon

这里有一个批处理脚本,你可以试着运行一下。我认为它能正确地捕捉空字符串。

这只是一个例子,你只需要根据你的脚本自定义2(或3?)个步骤即可。

@echo off
if not "%OS%"=="Windows_NT" goto EOF
:: I guess we need enableExtensions, CMIIW
setLocal enableExtensions
set i=0
set script=%0

:LOOP
set /a i=%i%+1

set A1=%1
if not defined A1 goto nomore

:: Assumption:
:: Empty string is (exactly) a pair of double-quotes ("")

:: Step out if str length is more than 2
set C3=%A1:~2,1%
if defined C3 goto goon

:: Check the first and second char for double-quotes
:: Any characters will do fine since we test it *literally*
if ^%A1:~1,1% == ^" if ^%A1:~0,1% == ^" goto blank
goto goon

:goon
echo.args[%i%]: [%1]
shift
goto LOOP

:blank
echo.args[%i%]: [%1] is empty string
shift
goto LOOP

:nomore
echo.
echo.command line:
echo.%script% %*

:EOF

此测试结果如下:
.test.bat :: ""  ">"""bl" " "< "">"  (")(") "" :: ""-"  " "( )"">\>" ""
args[1]: [::]
args[2]: [""] is empty string
args[3]: [">"""bl" "]
args[4]: ["< "">"]
args[5]: [(")(")]
args[6]: [""] is empty string
args[7]: [::]
args[8]: [""-"  "]
args[9]: ["( )"">\>"]
args[10]: [""] is empty string

command line:
.test.bat :: ""  ">"""bl" " "< "">"  (")(") "" :: ""-"  " "( )"">\>" ""

这是错误的,一个空字符串不是带有两个双引号 "" 的字符串,一个空字符串就是一个空字符串 ``,在批处理中,一个空变量是未定义的变量。你可以定义,对于你的参数,外部引号将被移除,但是这个字符串仍然是一个长度为0的空字符串。 - jeb
在这个上下文中(win cmd arguments),你不能用任何其他方式表示空字符串,甚至不能使用双单引号,在Unix shell下同样为空。 - user6801759
是的,"" 也用于表示空字符串,但使用 %~1 轻松去除外部引号。没有必要使用如此复杂的解决方案。 - jeb
你说得对,我可能表述不当,这不仅适用于空字符串,而且是一种安全枚举参数的方法。其他方式,如"%~1"==""将在像"Long File Name".txt这样的参数上失败,而这是一个有效的参数。 编辑:正如你所说,它绝对不像你说的那么复杂。测试只需要两个步骤,其余都是冗长的示例。 - user6801759
如果未定义ARG似乎工作得很好(我的变量不来自命令行) - GrayFace

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