如何将转义的双引号传递给批处理文件

4

我知道有类似的问题,但是在答案中找不到一个东西。我正在尝试将以下字符串传递给批处理文件:

hello " world

这是一个单一的参数。这是我的批处理文件:
@echo off
@echo %1

现在如果我从命令行运行它,我会得到以下结果:

>C:\file.bat "hello "" world"
"hello "" world"

>C:\file.bat "hello \" world"
"hello \"

>C:\file.bat "hello """ world"
"hello """

在所有情况下,我都得到了错误的结果,我该如何转义双引号并将其正确传递?或者在批处理文件本身中是否应执行任何其他转换步骤?

3
你可以尝试通过输入^"来实现。在批处理/cmd控制台中,“^”是转义字符。 - rojo
"\"不像你以为的那样是Windows控制台中的转义字符。 - phuclv
2个回答

7

由于两个原因,不可能将字符串文字值hello " world作为批处理参数传递:

  1. 无法在参数标记化方面转义空格。参数始终由未加引号的标记定界符分隔,即使它们是由^转义字符前导的也是如此。参数标记定界符为{space}{tab},;={0xFF}。包括定界符作为参数的唯一方法是确保定界符加上引号。

  2. 不可能在带引号的字符串中转义引号。引号是状态机:首次遇到引号会打开引号语义,后续的引号则关闭。下一个引号则再次打开,以此类推。如果关闭了引号,则可以使用^"来转义引号字面量以保持关闭引号。但是一旦开始引用,那么就没有办法转义结束引号:下一个引号总是关闭引用。

因此,无论您如何添加引号和/或转义引号,始终会存在一个未加引号的空格,因此该字符串将被视为两个参数。

您可以采用Hapax推荐的策略——引用整个字符串,并将字符串中的任何引号字面值加倍:"Hello "" world"

但是我会稍作修改,使用%~1而不是%1以删除外部引号。然后,应将加倍的引号转换回单引号:

@echo off
set "arg1=%~1"
set "arg1=%arg1:""="%"
echo %arg1%

但是将字符串字面量作为批处理参数传递可能存在其他潜在问题。最阴险的问题是插入字符(caret)会被加倍。如果调用者使用CALL,则无法将带引号的插入符作为参数传递。我不会深入介绍问题的机制。如果您想了解更多信息,请参见https://dev59.com/6G855IYBdhLWcg3w5Igb#4095133。但以下示例说明了问题:

test.bat

@echo %1

-- 示例cmd会话 --

D:\test>test "^"
"^"

D:\test>call test "^"
"^^"

由于传递字符串文字作为批处理参数时存在许多复杂性,在高级批处理脚本中使用的最有效策略是将值存储在环境变量中,然后将变量名称作为参数进行传递。批处理脚本可以使用延迟扩展来获取正确的值:

test2.bat

@echo off
setlocal enableDelayedExpansion
set "arg1=!%1!"
echo !arg1!

-- 示例cmd会话 --

D:\test>set "myVar=Hello world! ^&|<>" How are you doing? !^^^&^|^<^>^"

D:\test>set myVar
myVar=Hello world! ^&|<>" How are you doing? !^&|<>

D:\test>test2 myVar
Hello world! ^&|<>" How are you doing? !^&|<>

D:\test>call test2 myVar
Hello world! ^&|<>" How are you doing? !^&|<>

1
请纠正我如果我错了,但是在你的第一个代码块中,set "arg1=%arg1:"=%" 不应该改为 set "arg1=%arg1:""="%"(双倍内部双引号,并在尾部“%”之前添加一个双引号),以匹配Hapaxia的答案中的类似行。如此编写后,您将删除所有双引号(在剥离外部双引号之后),而不是将内部加倍双引号替换为单个双引号。也就是说,这将把"Hello""World"解析为HelloWorld而不是Hello"World - ShadowRanger
1
@ShadowRanger - 你说得完全正确。我写的一切都是正确的,但是我提供了与我的描述不符的无效代码。我已经按照你所说的更新了代码,以符合我的原始意图。感谢你的发现和报告。 - dbenham

2

保留引号的方法是将其传递两次。但这会传递两个引号。

您可以对输入进行处理,然后删除重复引号。

使用 file.bat "hello "" world" 执行时,我们使用:

@echo off
set param=%1
set param=%param:""="%
echo %param%

(结果:"hello " world")

简化版:

@echo off
set param=%1
echo %param:""="%

这是否意味着当我将某些内容传递给批处理文件时,我需要手动将任何转义序列转换为其原始形式? - username
天啊,我们仍然在努力理解什么情况下将单引号字符标记为脚本参数是有意义的。@用户名,我相信这个页面上提供的任何建议的应用完全取决于您的判断,因为只有您了解您计划的疯狂程度。对我来说,整个事情似乎就像一个XY问题 - rojo
@rojo 我的 X 问题是修复一段已经实现了 Y 功能的遗留软件。在我理解 Y 的问题之前,我无法向 X 寻求帮助。我曾尝试绕过这个问题,但似乎将参数传递给批处理文件的整个想法都有问题。 - username
4
你应该使用“set param=%~1”这个命令。你不希望外部引号成为变量值的一部分。 - dbenham
我同意。我点赞了你的答案,因为它包含了所有的信息和更多内容。 - Hapaxia
显示剩余4条评论

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