一个非常简单的检查是通过区分大小写的字符串比较一个环境变量的值来完成的替换为空字符串会导致删除所有双引号。
@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "mystring=Non quoted string"
set "myquotedstring="My quoted string""
setlocal EnableDelayedExpansion
set "_StringCheck=!mystring:"=!"
if !mystring! == !_StringCheck! (
echo mystring is defined with NOT quoted string: !mystring!
) else echo mystring is defined with quoted string: !mystring!
set "mystring="!_StringCheck!""
set "_StringCheck=!myquotedstring:"=!"
if !myquotedstring! == !_StringCheck! (
echo myquotedstring is defined with NOT quoted string: !myquotedstring!
) else echo myquotedstring is defined with quoted string: !myquotedstring!
set "myquotedstring="!_StringCheck!""
rem Output the two environment variables after string checking.
echo/
echo The environment variables mystring and myquotedstring are now:
echo/
set my
endlocal
endlocal
这段代码没有不必要的IF条件:
@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "mystring=Non quoted string"
set "myquotedstring="My quoted string""
setlocal EnableDelayedExpansion
set "mystring="!mystring:"=!""
set "myquotedstring="!myquotedstring:"=!""
echo The environment variables mystring and myquotedstring are:
echo/
set my
endlocal
endlocal
为什么要检查一个字符串是否被引用并添加引号?
几乎总是最好将字符串分配给不带引号的环境变量,并在批处理文件中使用引号,在引用环境变量的值时必须这样做,但在引用环境变量值时不带引号不想在 ECHO 线上。
其他答案包含轻松检查分配给环境变量的字符串是否包含在 " 中的解决方案,但它们都没有处理分配给环境变量的字符串只有一个 @987654328 的罕见用例@ 在开头或结尾,或者分配给环境变量的字符串只有一个字符,甚至可能是"。
批处理文件的作者很容易控制分配给环境变量的字符串在写入批处理文件本身的字符串上的样子。但是,如果通过参数字符串将字符串传递给批处理文件,或者提示用户输入分配给环境变量的字符串,则必须有额外的代码用于使批处理文件执行失败,因为错误的用户输入或参数错误字符串。
例子:
@echo off
setlocal EnableExtensions DisableDelayedExpansion
rem Example for processing an argument.
set "FirstArgument=%~1"
if defined FirstArgument set "FirstArgument=%FirstArgument:"=%"
setlocal EnableDelayedExpansion
echo First argument without quotes is: !FirstArgument!
endlocal
echo First argument with quotes is: "%FirstArgument%"
rem Example for processing a user input.
set "UserInput=""
set /P "UserInput=Please enter something: "
set "UserInput=%UserInput:"=%"
setlocal EnableDelayedExpansion
echo User input without quotes is: !UserInput!
endlocal
echo User input with quotes is: "%UserInput%"
endlocal
在这个例子中,首先需要的执行环境完全由前两个命令行定义:
- 命令回显模式已禁用
- 已启用命令扩展
- 已禁用延迟的环境变量扩展
命令SETLOCAL在根据使用的两个选项启用命令扩展和禁用延迟扩展之前,不仅将命令扩展和延迟扩展的当前状态推送到堆栈上,而且还会复制所有在启动批处理文件时已经定义的环境变量。这个新创建的环境变量列表用于批处理文件的其余部分。 SETLOCAL 还将当前目录的路径推入堆栈。
批处理文件末尾的命令ENDLOCAL恢复之前的执行环境,这意味着命令扩展和延迟扩展的状态从堆栈中弹出并进行相应设置。此外,创建的环境变量列表被丢弃并恢复初始环境变量列表,这意味着环境变量FirstArgument和UserInput在处理完该批处理文件后不再存在,分别在启动批处理文件时具有它们的初始值在启动批处理文件时已经存在的情况。还会从堆栈中弹出当前目录路径,如果在执行命令 ENDLOCAL 时仍然存在,则该目录再次设置为当前目录。
延迟的环境变量扩展被禁用,否则此演示批处理文件将错误地处理传递给包含一个或多个感叹号的批处理文件的参数。任何文件/文件夹名称都可以包含字符! 一次或多次。
处理传递给批处理文件的参数字符串
像Test.cmd这样的批处理文件可以按如下方式启动:
-
Test.cmd
批处理文件在启动时没有将参数字符串传递给批处理文件,这意味着 %1 被替换为空字符串。
-
Test.cmd C:\Temp\Example.txt
批处理文件以未用双引号括起来的参数字符串开头。
-
Test.cmd "C:\Temp\ ;%Development & Test!.txt"
批处理文件以一个用双引号括起来的参数字符串开头。
-
Test.cmd "C:\Temp\Common File Name.txt
批处理文件以参数字符串开头,其中错误地仅存在开头的 ",但缺少参数字符串末尾的 "。
-
Test.cmd C:\Temp\FileName.txt"
批处理文件以参数字符串开头,其中错误地缺少开头的 ",但存在参数字符串末尾的 "。
-
Test.cmd ""
批处理文件以空参数字符串开始。这种情况通常发生在可执行文件或其他批处理文件启动批处理文件时,该批处理文件在代码中包含错误导致启动批处理文件时在第一个参数字符串周围加上双引号,但缺少参数字符串。
-
Test.cmd "
批处理文件仅以一个双引号字符开始。这可能发生在使用德语或英式键盘的用户想要以 2 作为参数启动批处理文件,但按下 Shift+2 或 CapsLock 时启用按下 2 键,用户按太快 RETURN 或 ENTER 来执行命令行。
重要的是要知道,不可能将参数字符串传递给包含" 的批处理文件,而cmd.exe 应将其解释为文字字符。 Linux shell 解释器通过在参数字符串周围使用其他引号来支持这一点,但 Windows cmd.exe 不支持。
在大多数批处理文件中仅使用 "%~1" 在命令提示符窗口中运行时的帮助输出 call /? 进行了解释。这适用于大多数用例,但并非适用于所有用例,正如在上面的七个示例中测试此类批处理文件时所见。
发布的批处理文件将第一个参数分配给环境变量FirstArgument,让cmd.exe 删除周围的双引号。
环境变量FirstArgument 未为 first、sixth 和 last 示例定义为 @987654356在这种情况下,@ 被一个空字符串替换,所以 set "FirstArgument=" 被执行,这会导致删除环境变量(如果存在的话)。
环境变量FirstArgument是为second、third和fourth示例定义的,传递的参数字符串不带双引号。
但如果参数字符串开头没有",则cmd.exe 不会在使用%~1 时删除参数字符串末尾的"。因此,对于非常不寻常的 第五 用例,需要额外的代码来处理此类参数字符串也正确,而不会导致稍后由于语法错误而退出批处理文件处理,因为结尾处的 "字符串。
第五个用例的解决方案是从分配给环境变量FirstArgument的字符串中显式删除所有"(如果已定义此环境变量)。
需要引用字符串的字符列在 cmd 输出的帮助中&()[]{}^=;!'+,`~。如果应将参数字符串传递给包含重定向运算符 <>| 被解释为文字字符(例如密码字符串)的批处理文件,则参数字符串也必须用双引号引起来。
非常重要的是,环境变量FirstArgument 的赋值是用双引号括起来的命令SET 的参数variable=value 完成的。仅使用set FirstArgument=%~1 并不是万无一失的。为什么要使用set "variable=value",例如here。
根据提示处理用户输入的字符串
在批处理文件set /P 中使用与处理参数字符串时应注意相同,因为用户实际上可以输入任何内容。用户可以
- 不输入任何内容或
- 输入一个字符串,其中包含一个或多个
" 和/或
- 输入包含
&<>| 等运算符的字符串。
set /P 命令行上指定的环境变量要么在提示之前不存在,要么在提示之前已经存在的情况下仍然未定义,以防用户点击提示 RETURN 或 回车。
出于这个原因,最好在使用有用的默认字符串提示用户之前定义一个环境变量,或者在set /P 命令行之后立即检查if defined variable 条件是否用户输入了任何内容并处理它相应的用例。
环境变量UserInput在演示代码中使用单个"作为值在提示之前定义。因此,如果用户不输入任何内容,环境变量 UserInput 仍会以 " 作为值来定义。
set /P 之后的命令行会从用户输入字符串中删除所有"。现在的结果可能是在此命令行之后不再定义环境变量UserInput。应始终考虑到这一点。正常(再次)使用if defined variable 来处理删除所有双引号后不再定义的环境变量的用例是一种很好的做法。
使用 ECHO 输出环境变量字符串
在将包含环境变量引用%variable%本身的参数字符串用双引号括起来时删除所有双引号后,可以进一步安全地处理分配给环境变量的字符串。
但有时分配给环境变量的字符串(如不带或带路径的文件或文件夹名称)应该使用命令 ECHO 输出到控制台窗口或文件,并且应该在不包含环境的情况下完成双引号中的变量引用。
这很棘手,因为文件/文件夹名称可以包含像 & 这样的字符,在双引号参数字符串之外被 cmd.exe 解释为运算符,或者 ! 在延迟时被解释为环境变量引用的开始/结束目前已启用环境变量扩展。
第三个参数示例非常适合测试批处理文件,因为文件名以两个前导空格开头,接下来包含一个分号和一个百分号,另外还包含一个 & 和一个感叹号.有权使用任何文件/文件夹名称的批处理文件必须编码得非常好才能正确处理此类文件名。
演示代码分别输出用户输入的参数字符串,不带双引号,因此使用临时延迟的环境变量扩展,以避免传递给用户分别输入的批处理文件的字符串修改ECHO 命令行最终由cmd.exe 执行。测试演示批处理文件可以看出,很不寻常的文件名C:\Temp\ ;%Development & Test!.txt输出不带双引号,没有造成任何麻烦。
输出一个未知一致性的文件/文件夹名称并用双引号括起来当然更容易,因此更好。因此,每个带有 ECHO 信息的批处理文件打印,其中包含直接从文件系统读取的文件/文件夹名称,或作为参数传递给批处理文件或由用户在提示符下输入,都应包含文件/文件夹名称双引号更好。然后,用户也更容易看到文件/文件夹名称的开始和结束位置,特别是如果文件/文件夹名称输出时没有路径且前导空格。