在Windows批处理脚本中处理引号

79
在Windows批处理文件中,当您执行以下操作时:

set myvar="c:\my music & videos"

变量myvar将包含引号。实际上我认为这非常愚蠢。引号只是用来指示字符串的开始和结束位置,而不是作为值本身的一部分存储。
我该如何防止这种情况发生? 谢谢。

45
哦,我的天 - 你正在迈向真正令人发狂的命令行脚本领域的第一步。祝你好运 - 你将需要它。请参阅https://dev59.com/mkXRa4cB1Zd3GeqPrnDt#180767获取一本好书和一些建议的指引。 - Michael Burr
7个回答

77
set "myvar=c:\my music & videos"

注意引号是在 myvar 之前开始的。 这实际上很简单。 顺便说一句:如果没有用引号包围,就无法回显 myvar,因为 & 将被读取为命令分隔符,但它仍将作为路径工作。

http://ss64.com/nt/set.html 中的“变量名称可以包括空格”部分。


1
它其实是可以输出的,你只需要在引号中包含它 ;) echo "%myvar%" - Fowl
3
@Fowl 但是双引号也会被回显。 - binki
如果您像这样包装可执行文件的路径,则需要在调用时将其引用起来:“%msbuild%”。 - caduceus

66

这是正确的做法:

set "myvar=c:\my music & videos"

引号不会包含在变量值中。


这太棒了。我不知道这个。 - Jay Sullivan
11
非常有用,不过这个答案应该得到认可。https://dev59.com/LXRB5IYBdhLWcg3wuZY1#8582277 - user
2
你的答案和一年前写的 @RuskoGuyachev's 有何不同? - Matthieu
1
更清晰的表述是:“引号不会包含在变量值中。” - et_phonehome

36

这取决于您如何使用变量。如果您只想在不包括引号的情况下使用变量的值,则可以使用延迟扩展和字符串替换,或者使用 for 命令。

@echo OFF
SETLOCAL enabledelayedexpansion

set myvar="C:\my music & videos"

正如 andynormancx 所述,由于字符串包含 &,因此需要引号。或者您可以使用 ^ 进行转义,但我认为使用引号更加清晰。

如果您在字符串替换中使用延迟扩展,您将获得不带引号的变量值:

@echo !myvar:"=!
>>> C:\my music & videos

你也可以使用 for 命令:

for /f "tokens=* delims=" %%P in (%myvar%) do (
    @echo %%P
)
>>> C:\my music & videos

然而,如果你想在命令中使用该变量,你必须使用带引号的值或将变量值用引号括起来:

  1. 使用字符串替代和延迟扩展,在不带引号的情况下使用变量的值,但在命令中使用该变量:

@echo OFF
SETLOCAL enabledelayedexpansion

set myvar="C:\my music & videos"
md %myvar%
@echo !myvar:"=! created.
  • 使用for命令可以不用引号使用变量值,但是在命令中使用变量时必须加上引号:

  • @echo OFF
    set myvar="C:\my music & videos"
    
    for /f "tokens=* delims=" %%P in (%myvar%) do (
        md "%%P"
        @echo %%P created.
    )
    
    长话短说,在批处理文件中使用包含空格和/或 & 的路径或文件名是没有一个干净的方法。

    5
    优秀的回答,但是让我感到有些头痛。 - Binary Worrier
    2
    @Binary Worrier;是的,批处理文件往往会这样做;) - Patrick Cuff
    你一定是某种批处理文件之神。如此辉煌的聪明愚蠢 ;) - andynormancx

    7

    使用jscript。

    很久以前(约8年左右),我在一个大型C++/VB6项目上工作,我有各种批处理脚本来完成构建的不同部分。

    然后有人向我指出了Joel测试,我特别喜欢第2点,并开始将所有小的构建脚本合并为一个单独的构建脚本...

    这几乎让我心力憔悴,让所有这些小脚本在不同的机器上、具有稍微不同设置的情况下一起运行,设置变量和参数传递尤其困难。它非常脆弱,最轻微的事情都会导致它崩溃,并需要花费30分钟进行调整。

    最终-我可以很固执-我把所有东西都扔掉了,用JavaScript重新编写了所有内容,并从命令提示符中使用CScript运行。

    从那时起,我就没有回头看过去。虽然现在是MSBuild和Cruise Control,但如果我需要做任何稍微涉及到批处理脚本的事情,我就使用jscript。


    3
    虽然这并不是问题的答案,但既然我已经深入了解了批处理文件内外部的 cmd.exe 字符串转义问题,我完全同意你的看法。+1 - Tomalak
    4
    毋庸置疑,现如今所有的工作都要用 Powershell。 - Binary Worrier
    1
    @BinaryWorrier,你肯定会跟上时代,使用FAKE吧?(我不是在开玩笑,它真的很棒)(免责声明:我并没有完全确定你和/或OP是否在谈论构建脚本-只是出于跟踪者的目的!) - Ruben Bartelink

    7

    Windows命令解释器允许您在整个set命令周围使用引号(适用于从NT 4.0到Windows 2012 R2的所有Windows NT版本)。

    您的脚本应该按照以下方式编写:

    @echo OFF
    
    set "myvar=C:\my music & videos"
    

    那么您可以根据需要在变量周围加上引号。

    在CMD提示符下工作有时可能看起来很晦涩,但命令解释器实际上遵循其内部逻辑并表现得非常稳定,您只需要重新思考问题。

    事实上,set命令根本不需要您使用引号,但是您变量赋值的方式以及不使用引号的方法都可能导致您的变量周围存在额外的空格,在调试脚本时很难注意到这一点。

    例如,下面两种方式都是技术上有效的,但是您可能会有尾随空格,因此这不是一个好的做法:

    set myvar=some text   
    set myvar="some text" 
    

    例如,以下两种方法都可以在Windows命令解释器中设置变量,但双引号方法更为优秀:

    set "myvar=Some text"
    (set myvar=Some value)
    

    这两种方法都不需要解释,变量将会精确地包含你所需要的数据。

    加粗文本 然而,基于你使用了保留字符,只有引用的方法才能有效。

    因此,你应该使用:

    set "myvar=c:\my music & videos"
    

    然而,即使变量正确地设置为该字符串,当您ECHO字符串时,命令解释器将解释和符号为关键字,表示跟随另一个语句。

    因此,如果要从变量中回显字符串,则仍需告诉CMD解释器它是文本字符串,或者如果不想显示引号,则必须执行以下操作之一:

    用引号回显变量:

    Echo."%myvar%"
    

    输出变量但不包含引号:

    Echo.%myvar:&=^&%
    <nul SET /P="%myvar%"
    

    在上述两种情况下,您可以毫无问题地使用无引号的字符串进行回显。以下是示例输出:
    C:\Admin>    Echo.%myvar:&=^&%
    C:\my music & videos
    
    C:\Admin>    <nul SET /P="%myvar%"
    C:\my music & videos
    C:\Admin>
    

    2
    你可以始终使用延迟扩展来安全地使用变量。echo(!myvar! 输出时不需要引号。 - jeb

    1

    尝试使用转义字符'^',例如:

    set myvar=c:\my music ^& videos
    

    当你扩展myvar时,你必须小心,因为shell可能不会将&视为字面量。如果上述方法不起作用,请尝试在字符串中插入一个脱字符:

    set myvar=c:\my music ^^^& videos
    

    0

    两种解决方案:

    1. 在路径名(目录或文件名)中不要使用空格或其他对命令解释器特殊的字符。如果您只使用字母、数字、下划线和连字符(以及在扩展名前面用句点标识文件类型),那么您的脚本编写生活将变得非常简单。

      多年来,我编写并收集了大量工具,包括一个DOS实用程序,可以重命名文件。(它最初只是从文件名中删除空格的东西,但逐渐演变成可以在文件名中替换字符或字符串,甚至可以递归地进行。)如果有人感兴趣,我会把我的长期被忽视的网站重新启动并发布这个工具和其他工具。

      话虽如此,变量不仅仅是用于保存路径名,所以...

    2. 正如其他人已经指出的那样,SET "myvar=c:\my music & videos" 是这种变量值的正确解决方法。(是的,我说的是解决方法。我同意您最初的想法只是引用该值("my music & videos"),但正如他们所说,它就是这样的。


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