如何在“.bat”文件中检查命令行参数?

130

我的操作系统是Windows Vista。我需要一个“.bat”文件,其中我需要检查用户是否输入了任何命令行参数。如果输入了命令行参数且参数等于-b,则我将执行某些操作;否则,我将标记为“无效输入”。如果用户没有输入任何命令行参数,则我将执行另一些操作。我创建了以下.bat文件。它可以处理参数等于-b和不等于-b的情况,但在用户没有传递任何命令行参数时失败。

我总是收到错误:

GOTO was unexpected at this time.

有人能告诉我在这里我做错了什么吗?


ECHO OFF
CLS
ECHO.

IF [%1]==[/?] GOTO BLANK

IF %1=="-b" GOTO SPECIFIC

IF NOT %1=="-b" GOTO UNKNOWN

:SPECIFIC

ECHO SPECIFIC

GOTO DONE

:BLANK

ECHO No Parameter

GOTO DONE

:UNKNOWN

ECHO Unknown Option

GOTO DONE

:DONE

ECHO Done!

如果你在另外两个IF语句中加上括号(就像在GOTO BLANK行中一样),那么问题会得到解决吗? - Jeremiah Willcock
8个回答

180

您需要检查参数是否为空:if "%~1"=="" goto blank

完成上述步骤后,再对-b进行if/else开关:if "%~1"=="-b" (goto specific) else goto unknown

用引号括起来的参数可以使检查空/空白/缺失参数等事项更加容易。 "~"确保如果命令行参数上有双引号,则会去掉它们。


这个可行!只是“else”不被接受。我收到了错误信息:“else”不被识别为内部或外部命令、可操作的程序或批处理文件。所以我为“不等于-b”的情况添加了另一个IF。感谢您的快速回答。 - javauser71
7
ELSE 必须与 IF 在同一行,或者紧跟在括号后面。 - jeb
4
如果%1包含空格,对我来说似乎这个方法不起作用,可能是因为它是可执行文件的完整路径。参见Jeremiah Willcock的答案,他提供了一个即使参数值包含空格也能正常工作的解决方案。 - Mark A. Fitzgerald
如果您的参数是一个“文件”,那么您应该仅检查“存在”或“不存在”,否则它将不被接受。这就是为什么您的参数应该定义得很好,或者简单地使用PowerShell或VBScript(如果您在80年代..)。 - user257319
1
这对于 run.bat "a b" 失败。如果参数中有空格,"%1"=="" 会崩溃。请参见 https://dev59.com/4G445IYBdhLWcg3wRoHH#46942471。 - wisbucky

32

1
这不仅也能工作!它只是工作了,与"%1"==""形成对比。我必须在我的答案中详细说明。 - Tomasz Gandor
1
当 %1 被引用时,例如 foo.bat "第一个参数" 第二个参数,这将会出错。请参考 https://dev59.com/K3E85IYBdhLWcg3w8IM4 了解更多信息。 - matt wilkie
使用 [%1]==[-b] 将无法匹配带引号的参数。例如 run.bat "-b"。请参阅 https://dev59.com/4G445IYBdhLWcg3wRoHH#46942471。 - wisbucky
这对我有效,即使参数被引用也可以。例如 foo.bat "第一个参数" 在我的测试中运行良好。方括号似乎比 IF "%1"=="" 更好。 - David Pierson

28

简短回答 - 使用方括号:

if [%1]==[] goto :blank

或者(当需要处理带引号的参数时,请参见下面的编辑):

if [%~1]==[] goto :blank

为什么?你可能会问。嗯,就像Jeremiah Willcock提到的那样:http://ss64.com/nt/if.html - 他们使用这个!好的,但引号有什么问题吗?

简单回答:它们是“神奇”的 - 有时双引号会转换为单引号。而且它们需要匹配,首先。

考虑一下这个小脚本:

@rem argq.bat
@echo off

:loop 
if "%1"=="" goto :done
echo %1
shift
goto :loop

:done
echo Done.

让我们来测试一下:

C:\> argq bla bla
bla
bla
Done.

看起来能工作。但是现在,让我们切换到第二档:

C:\> argq "bla bla"
bla""=="" was unexpected at this time.

Boom 这不是真,也不是假。脚本已崩溃。如果你本应该关闭反应堆,那就太倒霉了。现在你会像哈里·达格利安一样死去。

你可能会认为 - 好吧,参数不能包含引号。如果它们包含,就会发生这种情况。错了 这里有一些安慰:

C:\> argq ""bla bla""
""bla
bla""
Done.

哦,是的。别担心 - 有时候 这个 生效。

让我们尝试另一个脚本:

@rem args.bat
@echo off

:loop 
if [%1]==[] goto :done
echo %1
shift
goto :loop

:done
echo Done.

你可以自行测试,对于上述情况它可以正常工作。这是符合逻辑的 - 引号与括号无关,因此这里没有什么魔法。但是如果用括号来增加参数呢?

D:\>args ]bla bla[
]bla
bla[
Done.

D:\>args [bla bla]
[bla
bla]
Done.

运气不好。括号无法阻止cmd.exe的解析器。

让我们回到邪恶引号的问题上。当参数以引号结尾时,问题就出现了:

D:\>argq "bla1 bla2"
bla2""=="" was unexpected at this time.

如果我只通过了这些呢:

D:\>argq bla2"
The syntax of the command is incorrect.
脚本根本无法运行。对于args.bat也是同样的情况:
D:\>args bla2"
The syntax of the command is incorrect.

但是如果在这种情况下 " 字符的数量“匹配”(即为偶数),我会得到什么:

D:\>args bla2" "bla3
bla2" "bla3
Done.

NICE - 希望您了解了关于.bat文件如何拆分其命令行参数的一些知识(提示:它与bash并不完全相同)。上述参数包含一个空格。但是引号不会被自动剥离。

那么argq呢? 它会有怎样的反应? 预料之中:

D:\>argq bla2" "bla3
"bla3"=="" was unexpected at this time.

所以,在你说:“你知道吗?只需使用引号(因为在我看来,这样更好看)”之前请先考虑。

编辑

最近有评论指出了这个答案 - 方括号“无法处理”传递带引号的参数,并将它们处理成未加引号的参数。

语法:

if "%~1"=="" (...)

这并不是双引号的新发现,而是展示了一个很好的特性,可以从参数变量中去除引号,如果第一个和最后一个字符是双引号。

这种“技术”同样适用于方括号:

if [%~1]==[] (...)

指出这一点非常有用,所以我也支持新答案。

最后,喜欢双引号的朋友们,你们的书中是否存在像""这样的参数,或者是空白的?只是问一下;)


1
方括号 [%1]==[-b] 如果参数被引用,如 run.bat "-b",则不会匹配。请参阅 https://dev59.com/4G445IYBdhLWcg3wRoHH#46942471。 - wisbucky
历史上的注释:在Win 98和早期的MS/PC-DOS系统批处理脚本中,[%1]==[-b]"%1"=="-b"是相同的。自从Win 2000/NT引入了语法if "%~1"=="-b",其中双引号具有特殊含义,你应该编写这样的脚本,因为它提供了更强大的保护。双引号可以转义特殊字符的含义(尝试在命令行中使用&、|和%字符)。99.9%的示例都使用双引号语法 - 你的示例argq bla2" "bla3是唯一需要用到方括号的情况。嵌入式双引号是灾难的源头 - 只是这么说。 - Skip R
@SkipR - 这就是为什么在Windows文件系统中,你不能在名称中使用这些特殊字符的原因。我没有数学证明,但我认为并不是所有东西都可以被引用(以某种转义序列等方式直接表示)在cmd shell中。在其他系统中,有时可以引用所有内容,包括NUL字符。(https://dev59.com/SXE85IYBdhLWcg3whUBr)- 这个问题只是表明,你需要使用一些外部程序。 - Tomasz Gandor
参数中带有等号("key=value") 时会导致失败。我通过在变量中替换它们(https://dev59.com/wWHVa4cB1Zd3GeqPjybe#51827258)来解决我的问题,然后再检查它是否为空。 - Ben Philipp

8

除了其他回答,我赞同的是,您可以考虑使用IF命令的/I开关。

...如果指定了/I开关,则表示执行不区分大小写的字符串比较。

如果您希望为用户提供不区分大小写的灵活性来指定参数,则可能会有所帮助。

IF /I "%1"=="-b" GOTO SPECIFIC

7
你正在比较字符串。如果省略了参数,%1 会扩展为空格,使得命令变成例如 IF =="-b" GOTO SPECIFIC (这是语法错误)。请使用引号(或方括号)将字符串包起来。
REM this is ok
IF [%1]==[/?] GOTO BLANK

REM I'd recommend using quotes exclusively
IF "%1"=="-b" GOTO SPECIFIC

IF NOT "%1"=="-b" GOTO UNKNOWN

IF [%1]==[/?] 不起作用,但如果我使用 IF [%1]==[] 或者 "%1"=="",那么它就可以工作了。无论如何,现在我可以继续了。感谢您的快速回复。 - javauser71

7

实际上,其他所有答案都有缺陷。最可靠的方法是:

IF "%~1"=="-b" (GOTO SPECIFIC) ELSE (GOTO UNKNOWN)

详细解释:

如果传递带有空格和引号的参数,则使用 "%1"=="-b" 将直接崩溃。这是最不可靠的方法。

IF "%1"=="-b" (GOTO SPECIFIC) ELSE (GOTO UNKNOWN)

C:\> run.bat "a b"

b""=="-b" was unexpected at this time.

使用[%1]==[-b]更好,因为它不会与空格和引号冲突,但如果参数被引号包围,则不会匹配。

IF [%1]==[-b] (GOTO SPECIFIC) ELSE (GOTO UNKNOWN)

C:\> run.bat "-b"

(does not match, and jumps to UNKNOWN instead of SPECIFIC)

使用 "%~1"=="-b" 是最可靠的。如果存在引号,%~1 会将其去除。因此它可以在有或无引号以及没有参数的情况下工作。

IF "%~1"=="-b" (GOTO SPECIFIC) ELSE (GOTO UNKNOWN)

C:\> run.bat
C:\> run.bat -b
C:\> run.bat "-b"
C:\> run.bat "a b"

(all of the above tests work correctly)

1
这并不是方括号的终点,你看:IF [%~1]==[] - 这个可以工作,甚至可以处理 "-b"。你只需要能够在盒子里面思考,嗯,方括号里面思考。 - Tomasz Gandor

3

最近我在批处理文件中遇到了复杂参数开关的实现问题,下面是我的研究结果。提供的答案都不是完全安全的,例如:

"%1"=="-?" 如果参数被引号括起来(例如文件名),将无法匹配,或者如果参数被引号包含并且带有空格(通常见于文件名),则会崩溃。

@ECHO OFF
SETLOCAL
echo.
echo starting parameter test...
echo.
rem echo First parameter is %1
if "%1"=="-?" (echo Condition is true, param=%1) else (echo Condition is false, param=%1)
C:\>test.bat -?

starting parameter test...

Condition is true, param=-?

C:\>test.bat "-?"

starting parameter test...

Condition is false, param="-?"

任何带有方括号[%1]==[-?][%~1]==[-?]的组合都会在参数中有引号的空格情况下失败:
@ECHO OFF
SETLOCAL 
echo.
echo starting parameter test...
echo.
echo First parameter is %1
if [%~1]==[-?] (echo Condition is true, param=%1) else (echo Condition is false, param=%1)

C:\>test.bat "long file name"

starting parameter test...

First parameter is "long file name"
file was unexpected at this time.

提议的最安全解决方案"%~1"=="-?",会因为包含引号外文本和引号内带空格的复杂参数而崩溃:
@ECHO OFF
SETLOCAL 
echo.
echo starting parameter test...
echo.
echo First parameter is %1
if "%~1"=="-?" (echo Condition is true, param=%1) else (echo Condition is false, param=%1)

C:\>test.bat -source:"long file name"

starting parameter test...

First parameter is -source:"long file name"
file was unexpected at this time.

确保覆盖所有上述情况的唯一方法是使用EnableDelayedExpansion并通过引用(而不是值)使用变量传递参数。这样即使最复杂的情况也能正常工作:

@ECHO OFF
SETLOCAL EnableDelayedExpansion
echo.
echo starting parameter test...
echo.
echo First parameter is %1
:: we assign the parameter to a variable to pass by reference with delayed expansion
set "var1=%~1"
echo var1 is !var1!
:: we assign the value to compare with to a second variable to pass by reference with delayed expansion
set "var2=-source:"c:\app images"\image.png"
echo var2 is !var2!
if "!var1!"=="!var2!" (echo Condition is true, param=!var1!) else (echo Condition is false, param=!var1!)
C:\>test.bat -source:"c:\app images"\image.png

starting parameter test...

First parameter is -source:"c:\app images"\image.png
var1 is -source:"c:\app images"\image.png
var2 is -source:"c:\app images"\image.png
Condition is true, param=-source:"c:\app images"\image.png

C:\>test.bat -source:"c:\app images"\image1.png

starting parameter test...

First parameter is -source:"c:\app images"\image1.png
var1 is -source:"c:\app images"\image1.png
var2 is -source:"c:\app images"\image.png
Condition is false, param=-source:"c:\app images"\image1.png

C:\>test.bat -source:"c:\app images\image.png"

starting parameter test...

First parameter is -source:"c:\app images\image.png"
var1 is -source:"c:\app images\image.png"
var2 is -source:"c:\app images"\image.png
Condition is false, param=-source:"c:\app images\image.png"

1
这个问题已经有一些非常好的答案,但所有的答案似乎都坚持使用goto
如果您不想在2023年使用Windows 10中使用goto稍微复杂的情况下,您可以简单地在if语句之后使用括号()
因此,您肯定可以创建像这样的批处理文件:
@echo off

if "%~1"=="full" (
echo hello
echo hello again
)

echo hello always

如果您使用以下命令行执行此操作:if_argument_test.bat full,您将看到:
hello
hello again
hello always

如果你不带上 full 参数执行它,你会看到这个:

hello always

没有goto,批处理代码会更加简洁。


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