测试批处理文件中参数是不是为空的正确方法是啥?

Posted

技术标签:

【中文标题】测试批处理文件中参数是不是为空的正确方法是啥?【英文标题】:What is the proper way to test if a parameter is empty in a batch file?测试批处理文件中参数是否为空的正确方法是什么? 【发布时间】:2011-02-02 06:17:00 【问题描述】:

我需要测试是否设置了变量。我尝试了几种技术,但每当%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.

根据this site,这些是受支持的IF 语法类型。所以,我看不出有什么办法。

IF [NOT] ERRORLEVEL number command
IF [NOT] string1==string2 command
IF [NOT] EXIST filename command

更新:在 2020 年 10 月 25 日,我将接受的答案从使用括号更新为使用波浪号。每个人都说波浪号更好,因为它更安全。我有点心烦意乱,因为波浪号看起来更复杂,而且不太清楚它的目的是什么,但我还是改变了它。

【问题讨论】:

在我的系统(Windows 2003 和 Windows 7)上,if "%1" == "" GOTO MyLabel 不会致命地终止脚本的执行,只要 %1 有偶数个双引号。我看到%1 中的奇数个双引号会导致脚本的执行出现此错误:The syntax of the command is incorrect. 下面使用方括号解决问题的解决方案已被标记为正确答案,但事实并非如此似乎做得更好。当%1 有奇数个双引号时,该解决方案也会失败并出现相同的错误。 @SusamPal 有趣。试试它下面的括号版本,看看是否有效。那个我测试了更多。几天前我刚刚更新了接受的答案。 Dan Story's answer 似乎确实可以正常工作。我使用方括号的版本。 一个很好的“包罗万象”示例:***.com/questions/830565/… 涵盖文件/目录和通用字符串/数字混合参数。 太令人沮丧了——IF DEFINED 只处理环境变量而不是脚本变量真是浪费潜力! 【参考方案1】:

使用方括号代替引号:

IF [%1] == [] GOTO MyLabel

括号不安全:只能使用方括号。

【讨论】:

括号不安全! %1中的&之类的内容会断行 使用if #%1#==##if [%1]==[]等其他字符是好的,只要参数中没有空格,否则会出现…was unexpected at this time错误。 正如我在回答中提到的那样,使用"%~1" 确实是一种更好的方法。 伙计们,@jamesdlin 是正确的。如果变量文本有空格或者是==,这种方法将不起作用。改用他的答案。 你没有说清楚it DOES NOT work with spaces。请改用@jamesdlin 答案。【参考方案2】:

你可以使用:

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

去除外部引号。一般来说,这是一种比使用方括号更可靠的方法,因为即使变量中有空格,它也可以工作。

【讨论】:

这确实是一个好方法。通过使用~,您可以去掉外部引号(如果存在),然后手动添加它们(确保只有一组)。此外,如果参数包含空格,您必须使用引号(当我以前使用# 或括号而不是引号的版本导致带有空格的参数抛出…was unexpected at this time 时,我很难学会这一点错误。) 这是否也适用于像 %MYVAR% 这样的一般变量而不是像 %1 这样的参数? @simpleuser 哎呀,你是对的,它不适用于一般环境变量。 注意使用双 '"' 作为转义序列的字符串 -- 尝试比较时脚本会崩溃。例如 ""this string will crash the comparison""。双 '"' 是正确的转义序列,并且正在做如果字符串为空,对双 '"' 的替换将失败。 @Amalgovinus 您可能可以将变量作为参数传递给子程序并让子程序使用"%~1"【参考方案3】:

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

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"" 也很有用。 它利用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!'

如果您想 100% 防弹以获取内容,您可以阅读 How to receive even the strangest command line parameters?

【讨论】:

【参考方案4】:

来自 IF /?:

如果启用命令扩展 IF 变化如下:

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

......

DEFINED 条件仅适用 像 EXISTS 一样,除了它需要一个 环境变量名和返回 如果环境变量为 true 已定义。

【讨论】:

是的,我实际上尝试过这种方法,据我所知,这仅适用于 ENVIRONMENT 变量。因此,不适用于 %1 或批处理文件中定义的变量。 这仅适用于%1 等。 “在批处理文件中定义的变量”实际上批处理文件运行时的环境变量,您可以在它们上使用IF DEFINED【参考方案5】:

不幸的是,我没有足够的声誉来评论或投票对我必须自己编写的当前答案。

最初,OP 的问题是“变量”而不是“参数”,这变得非常混乱,特别是因为这是谷歌搜索如何测试空白变量的第一链接。自从我的原始答案以来,Stephan 已经编辑了原始问题以使用正确的术语,但我决定保留它以帮助消除任何混淆,而不是删除我的答案,尤其是在谷歌仍然向这里发送变量的情况下:

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

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

变量使用 set 命令设置,并使用 2 个百分号调用 - 一个之前和一个之后。例如 %myvar%

要测试空变量,请使用“如果未定义”语法(显式用于变量的命令不需要任何百分号),例如:

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 的答案。)

【讨论】:

你是对的。但正确的方法是将标题从if variable is empty 更正为if parameter is empty。我刚刚做完。 (冒着使某些答案无效的风险,这些答案确实检查了变量而不是参数,因此无论如何都是无效的,因为他们没有回答问题) 好的,谢谢斯蒂芬,没想到你能做到。我已经编辑了答案以反映更新。【参考方案6】:

使用“IF DEFINED 变量命令”来测试批处理文件中的变量。

但如果你想测试批处理参数,请尝试以下代码以避免棘手的输入(例如“1 2”或ab^>cd)

set tmp="%1"
if "%tmp:"=.%"==".." (
    echo empty
) else (
    echo not empty
)

【讨论】:

【参考方案7】:

我根据这里的答案创建了这个小批量脚本,因为有很多有效的。只要您遵循相同的格式,请随意添加:

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

【讨论】:

【参考方案8】:

你可以使用

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

【讨论】:

欢迎来到 SO!你的回答似乎是对这个问题的一个很好的回答。一旦您拥有足够的 [声誉] (***.com/help/whats-reputation),您就可以在任何帖子上使用 comment。还要检查这个what can I do instead。【参考方案9】:

我用下面的代码测试,没问题。

@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 是有原因的【参考方案10】:

我通常用这个:

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

如果 %1 为空,IF 将比较 "."到 ”。”这将评估为真。

【讨论】:

这是不正确的。这与以下内容相同: IF "%1" == "" 这篇文章的原因是如果 %1 本身有引号,则会失败。 您是否尝试测试 %1 是否为空或是否有引号?问题不清楚。如果在命令行上没有为 %1 指定任何内容,我的答案就有效。 > 您是否要测试 %1 是否为空或是否有引号? @aphoria,它必须能够同时处理两者。 我用它来计算是否设置了 %1%,对我来说效果很好。谢谢,很好的答案! aphoria - 请原谅 10 年后的回应,但我今天才读到这个。扩展@blak3r 所说的内容...例如,您的批处理文件名为batch.cmd。用最简单的话来说,你执行:batch.cmd "。也就是说,只有一个参数由一个 双引号 标记组成。或batch.cmd ab"cd,或任何更复杂的东西,通常被描述为具有奇数个["] 标记。您的语句:IF "%1."=="." GOTO MyLabel 将失败并导致批处理执行结束(崩溃),通常会出现错误:The syntax of the command is incorrect.,因为 ["] 标记无法匹配。【参考方案11】:

空字符串是一对double-quotes/"",我们可以只测试长度:

set ARG=%1
if not defined ARG goto nomore

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

然后针对 double-quotes 测试它的 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 在这种情况下(win cmd 参数),你不能用任何其他方式表示一个空字符串,即使是在 unix shell 下同样为空的双单引号也是如此。 是的,"" 也用于空字符串,但使用%~1 删除外部引号非常容易。没必要用这么复杂的方案 你说得对,我可能说得不恰当,它不仅适用于空字符串,而且是一种枚举参数的安全方法。 "%~1"=="" 之类的其他方式将失败,并出现 "Long File Name".txt 之类的参数,这是一个有效的参数。 edit:而且绝对没有你说的那么复杂。上面只有 2 个步骤进行测试,其余的都是详细示例。 "if not defined ARG" 似乎工作得很好(我的变量不是来自命令行)【参考方案12】:

脚本 1:

输入(“删除引号.cmd”“这是一个测试”)

@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:

输入(“删除引号.cmd”“这是一个测试”)

@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 . . .

【讨论】:

【参考方案13】:

总结一下:

set str=%~1
if not defined str ( echo Empty string )

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

【讨论】:

【参考方案14】:

我遇到了很多问题,网上有很多答案。大多数都适用于大多数事情,但总有一个角落案例会破坏每个案例。 如果它有引号它可能不起作用,如果它没有引号它可能会中断,如果 var 有空格,则语法错误,有些只适用于参数(而不是环境变量),其他技术允许一个空一组引号作为“定义”传递,一些更复杂的引号不会让你在之后链接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

回显nynyy


更多示例:

只想查看ifCall :ifSet %var% &amp;&amp; Echo set 如果不是(只有其他)? Call :ifSet %var% || Echo set 检查传递的参数;工作正常。 Call :ifSet %1 &amp;&amp; Echo set 不想阻塞你的脚本/复制代码,所以你把它放在它自己的ifSet.bat 中?没问题:((Call ifSet.bat %var%) &amp;&amp; Echo set) || (Echo not set)

【讨论】:

找到一个案例;如果您尝试使用 else 语法的 if,并且 then 块中的最后一个命令失败,则 else 将执行:(Call ifSet.bat YES &amp;&amp; (Echo true &amp; Echo x | findstr y)) || Echo, echos true and false 实际上,如果这是一个问题,并且您可以处理额外的行,您可以通过自己调用ifSet,然后使用If %errorlevel% EQU 0 (x) Else (y)来缓解这种情况 接受的答案(使用方括号)在哪些情况下不起作用?你能解释一下 Exit /B 的作用吗 由于某种原因,当我使用set 变量(如[%bob%],仅适用于[%2] 之类的参数)时,我测试它崩溃了,但现在重新检查它是否有效。我想我现在对[] 的唯一抱怨是它允许空引号通过,而这不会(所以这取决于你的用例,真的) /B 标志阻止整个批处理退出,仅退出子例程或call'd 批处理。除非你问whole command 在做什么;它返回一个错误代码,让调用脚本知道子程序的结果(0 通过,1 失败),可通过答案中的布尔逻辑或通过上述 cmets 中的%errorlevel% 使用【参考方案15】:

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

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

【讨论】:

小费真的很糟糕。引号至少可以处理空格和特殊字符,但感叹号不能。当启用延迟扩展时,感叹号在这里变得非常讨厌【参考方案16】:
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).

【讨论】:

但由于myBatch ^&amp; 的语法错误而失败。顺便提一句。使用[]是没用的,它们没有特殊含义,也逃不掉任何东西。【参考方案17】:

最简单的解决方案,由两行代码组成:

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

【讨论】:

简单但错误。使用set "var01=User&amp;Doc"set var01=---set var01=1 0 0 进行测试 你是对的。我的示例仅检查变量是否存在,而不检查变量是否为空。我误解了主题任务。【参考方案18】:

我刚出生不到一个月(尽管 8 年前有人问过)...我希望他/她现在已经超越了批处理文件。 ;-) 我以前一直这样做。不过,我不确定最终目标是什么。如果他/她像我一样懒惰,那么我的 go.bat 就可以处理这样的事情。 (见下文) 但是,1,如果您直接将输入用作命令,则 OP 中的命令可能无效。即,

"C:/Users/Me"

是一个无效的命令(或者如果您在不同的驱动器上曾经是)。你需要把它分成两部分。

C:
cd /Users/Me

还有,2,“已定义”或“未定义”是什么意思? 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" (and the proper path delimeter for Windows is `,而不是 /)。 这是一个很长的帖子,但甚至没有尝试回答这个问题。这是关于如何检测空参数,而不是what could be a default behaviour for an empty argument

以上是关于测试批处理文件中参数是不是为空的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

如何检测文件夹是不是为空(Windows 批处理文件)?

使用批处理命令检查文件夹是不是为空?

在批处理文件中执行子字符串的最佳方法是啥?

“X 不是内部或外部命令、可运行程序或批处理文件”是啥原因?

Windows批处理复制命令是啥?

检查字符串是不是为空或为空的最简单方法