eplawless 自己的回答简单有效地解决了他的具体问题:它将"整个参数列表中的所有实例替换为\",这就是 Bash 需要在双引号字符串中使用双引号来表示的方式。
要普遍回答如何使用Windows 命令行解释器在双引号字符串中转义双引号的问题(无论是在命令行上 - 通常仍被错误地称为“DOS 提示符” - 还是在批处理文件中):cmd.exe请参阅底部以了解PowerShell。
tl;博士:
答案取决于您调用的程序:
将参数传递给(另一个)批处理文件时必须使用"",并且可以与使用Microsoft的 C/C++/.NET 编译器(也接受)创建的应用程序一起使用,在 Windows 上包括 Python、Node.js和PowerShell (核心)7+ 的CLI()但不是 Windows PowerShell 的():""\"pwshpowershell.exe
- 例子:
foo.bat "We had 3"" of rain."
以下内容仅适用于定位批处理文件:
\"是许多其他程序(例如,Ruby、Perl、PHP 以及使用 CommandLineToArgvWindows API 函数解析其命令行参数的程序)所必需的 - 作为唯一选项,但它使用 fromcmd.exe并不健壮和安全:
\"是许多可执行文件和解释器需要的——包括 Windows PowerShell——当从外部传递字符串时,在命令行上——或者,在微软的编译器的情况下,支持作为替代方案 ""——不过,最终取决于目标程序解析参数列表。
- 例子:
foo.exe "We had 3\" of rain."
- 但是,使用
\"can中断调用并至少假设会导致不必要的、任意执行命令和/或输入/输出重定向:
- 以下字符存在此风险:
& | < >
- 例如,以下结果会导致
ver命令的意外执行;请参阅下面的进一步解释和解决方法的下一个要点:
foo.exe "3\" of snow" "& ver."
- 对于调用Windows PowerShell CLI,
powershell.exe和是强大但有限的替代方法\""("^""请参阅下面的“调用 PowerShell 的 CLI ...”部分)。
如果你必须使用\"from cmd.exe,只有 3 种安全的方法 fromcmd.exe,但是很麻烦:向TS 致敬以寻求帮助。
在批处理文件中使用(可能是选择性的)延迟变量扩展,您可以将文字存储\"在变量中并使用语法在"..."字符串中引用该变量!var!- 请参阅T S 的有用答案。
- 上述方法尽管很麻烦,但其优点是您可以有条不紊地应用它,并且它可以在任何输入下稳健地工作。
只有使用 LITERAL 字符串 - 不涉及 VARIABLES 的字符串 - 你会得到类似的有条不紊的方法: categorically ^-escape all cmd.exe metacharacters: " & | < >和 - 如果你还想抑制变量扩展 - %:
foo.exe ^"3\^" of snow^" ^"^& ver.^"
否则,您必须根据识别字符串的哪些部分由于被误解cmd.exe\"为关闭分隔符而被视为未引用来制定您的字符串:
背景
注意:这是基于我自己的实验。如果我错了,请告诉我。
类 POSIX 的 shell,例如类 Unix 系统上的 Bash,在将参数单独传递给目标程序之前对参数列表(字符串)进行标记:在其他扩展中,它们将参数列表拆分为单个单词(分词)并从结果词(引号删除)。目标程序将收到一组单独的逐字参数,即删除了句法引号。
相比之下,Windows 命令解释器显然不会对参数列表进行标记,而只是传递包含所有参数的单个字符串- 包括引用字符。- 到目标程序。
但是,在将单个字符串传递给目标程序之前会进行一些^预处理:转义字符。双引号之外的字符串被删除(它们转义以下字符。),变量引用(例如,%USERNAME%)首先被插值。
因此,与 Unix 不同,目标程序负责解析参数字符串并将其分解为去掉引号的单个参数。因此,不同的程序可能需要不同的转义方法,并且没有单一的转义机制可以保证适用于所有程序- https://stackoverflow.com/a/4094897/45375 包含关于 Windows 命令行解析的无政府状态的优秀背景.
在实践中,\"这是非常常见的,但不是安全的cmd.exe,如上所述:
由于cmd.exe它本身不能识别\"为转义的双引号,因此它可能会将命令行上的后续标记误解为未引用,并可能将它们解释为命令和/或输入/输出重定向。
简而言之:问题浮出水面,如果以下任何字符跟随一个开口或不平衡 \":& | < > ; 例如:
foo.exe "3\" of snow" "& ver."
cmd.exe看到以下标记,这是由于误解\"为常规双引号引起的:
"3\"
of
snow" "
- 休息:
& ver.
由于cmd.exe认为那& ver.是unquoted,它将其解释为&(命令序列运算符),后跟要执行的命令的名称(ver.-.被忽略;ver报告cmd.exe的版本信息)。
整体效果是:
- 首先,仅使用前3 个标记
foo.exe调用。
- 然后,执行命令
ver。
即使在意外命令没有伤害的情况下,您的整体命令也不会按设计工作,因为并非所有参数都传递给它。
许多编译器/解释器只识别\"- 例如,GNU C/C++ 编译器、Perl、Ruby、PHP,以及使用 CommandLineToArgvWindows API 函数解析其命令行参数的程序 - 对他们来说,没有简单的解决方案问题。
本质上,您必须事先知道命令行的哪些部分被误解为未引用,并有选择地^转义这些部分中的所有实例& | < >。
相比之下,使用""is SAFE,但遗憾的是仅支持基于 Microsoft 编译器的可执行文件和批处理文件(在批处理文件的情况下,具有上面讨论的怪癖),值得注意的是不包括PowerShell - 请参阅下一节。
cmd.exe从类似 POSIX 的 shell调用 PowerShell 的 CLI :
注意:有关如何在PowerShell中处理引用,请参阅底部部分。
从外部调用时- 例如, from cmd.exe,无论是从命令行还是批处理文件:
PowerShell [Core] v6+现在可以正确识别""(除了\"),它既可以安全使用又可以保留空白。
pwsh -c " ""a & c"".length "不会破坏并正确产生6
Windows PowerShell(最新和最终版本为 5.1 的旧版) 仅识别or \"""",后者是 , 形式中最可靠的选择cmd.exe"^"""(即使内部PowerShell 使用`双引号字符串中的转义字符并且也接受""- 请参阅底部),如下所述:
从/ 批处理文件调用Windows PowerShell : cmd.exe
"" break,因为它基本上不受支持:
powershell -c " ""ab c"".length "-> 错误“字符串缺少终止符”
\"原则上工作""" ,但不安全:
powershell -c " \"ab c\".length "按预期工作:它输出5(注意2 个空格)
- 但这并不安全,因为
cmd.exe元字符会破坏命令,除非转义:
powershell -c " \"a& c\".length " breaks,由于&,必须将其转义为^&
\""是安全的,但规范化内部空白,这可能是不希望的:
powershell -c " \""a& c\"".length "输出4(!),因为 2 个空格被归一化为 1。
"^""特别是Windows PowerShell的最佳选择,它既安全又保留空白,但对于 PowerShell Core(在 Windows 上),它与 相同\"",即空白规范化(如上所述,只需""在此处使用)。归功于 Venryx发现了这种方法。
在类 Unix 平台(Linux、macOS)上,当从类 POSIX 的 shell 调用PowerShell [Core]的 CLI 时,pwsh例如bash:
您必须使用\",但是它既安全又保留空白:
$ pwsh -c " \"a& c|\".length" # OK: 5
相关信息
^只能用作不带引号的字符串中的转义字符- 在双引号字符串中,不特殊^并被视为文字。
- CAVEAT:传递给语句的 in 参数的使用
^call被破坏(这适用于以下两种用法call:调用另一个批处理文件或二进制文件,以及调用同一批处理文件中的子例程):
^双引号中的实例被莫名其妙地加倍,改变了传递的值:例如,如果变量%v%包含文字值a^b,则将(!)call :foo "%v%"分配给子例程中的(第一个参数)。"a^^b"%1:foo
- 不带引号的
^with使用完全call被破坏,因为^它不能再用于转义特殊字符:例如,call foo.cmd a^&b悄悄地中断(而不是传递文字a&b就像foo.cmd没有 的情况一样call) -foo.cmd甚至从未调用(!),至少在 Windows 上7.
不幸的是,转义文字%是一种特殊情况,它需要不同的语法,具体取决于字符串是在命令行还是在批处理文件中指定的;见https://stackoverflow.com/a/31420292/45375
- 简而言之:在批处理文件中,使用
%%. 在命令行中,%不能进行转义,但如果将 a放在不带引号的字符串(例如, )^的开头、结尾或变量名内部,可以防止变量扩展(插值);命令行上不属于变量引用的实例被视为文字(例如,)。echo %^foo%%100%
通常,要安全地使用可能包含空格和特殊字符的变量值:
- 赋值:将变量名和值括在一对双引号中;例如,
set "v=a & b"将文字值分配a & b给变量%v%(相比之下,set v="a & b"会使双引号成为值的一部分)。将文字%实例转义为%%(仅适用于批处理文件 - 见上文)。
- 参考:双引号变量引用以确保它们的值没有被插值;例如,
echo "%v%"不会对 的值%v%进行插值和打印"a & b"(但请注意,双引号也总是会打印)。相比之下,echo %v%将文字传递a给echo,解释&为命令序列运算符,因此尝试执行名为 的命令b。
还要注意上面的警告重用^withcall语句。
- 外部程序通常负责删除参数周围的双引号,但如前所述,在批处理文件中您必须自己做(例如,
%~1从第一个参数中删除双引号),遗憾的是,没有直接的我知道在没有封闭双引号的情况下echo忠实地打印变量值的方式。
- Neil提供了一个
for基于 - 的解决方法,只要该值没有嵌入双引号就可以使用;例如:
set "var=^&')|;,%!" for /f "delims=" %%v in ("%var%") do echo %%~v
cmd.exe不将单引号识别为字符串分隔符 ( '...') - 它们被视为文字,通常不能用于分隔带有嵌入空格的字符串;同样,紧靠单引号的标记和介于两者之间的任何标记都被视为未引用cmd.exe并相应地解释。
- 然而,鉴于目标程序最终会执行自己的参数解析,一些程序(例如 Ruby)即使在 Windows 上也能识别单引号字符串。相比之下,C/C++ 可执行文件和 Perl无法识别它们。
但是,即使目标程序支持,也不建议使用单引号字符串,因为它们的内容不受cmd.exe.
从PowerShell中引用:
Windows PowerShell是一个比 PowerShell 高级得多的 shell cmd.exe,它多年来一直是 Windows 的一部分(PowerShell Core也将 PowerShell 体验带到了 macOS 和 Linux)。
PowerShell在引用方面始终在内部工作:
- 在双引号字符串中,使用
`"或""转义双引号
- 在单引号字符串中,用于
''转义单引号
这适用于 PowerShell 命令行以及从PowerShell 中将参数传递给 PowerShell 脚本或函数时。
(如上所述,将转义的双引号从外部传递给 PowerShell需要\",或者更稳健地\""- 没有其他方法)。
遗憾的是,当从 PowerShell 调用外部程序时,您既需要适应 PowerShell 自己的引用规则,又需要为目标程序转义:
- 此答案中也讨论和总结了这种有问题的行为;PowerShell Core 7.2.0-preview.5 中引入的实验性 功能 - 假设它成为官方功能 - 将至少为那些接受.
PSNativeCommandArgumentPassing \"
双引号内的双引号字符串:
考虑 string "3`" of rain",它在 PowerShell 内部转换为 literal 3" of rain。
如果要将此字符串传递给外部程序,除了PowerShell 的;之外,还必须应用目标程序的转义。假设您想将字符串传递给 C 程序,该程序期望嵌入的双引号被转义为\":
foo.exe "3\`" of rain"
请注意- 让 PowerShell 快乐 -和 -让目标程序快乐 - 必须存在。`"\
相同的逻辑适用于调用批处理文件,其中""必须使用:
foo.bat "3`"`" of rain"
相比之下,在双引号字符串中嵌入单引号根本不需要转义。
单引号字符串中的单引号不需要额外的转义;考虑'2'' of snow',这是 PowerShell 的2' of snow.
foo.exe '2'' of snow'
foo.bat '2'' of snow'
PowerShell 在将单引号字符串传递给目标程序之前将其转换为双引号字符串。
但是,单引号字符串中的双引号,对于PowerShell不需要转义,但对于目标程序仍然需要转义:
foo.exe '3\" of rain'
foo.bat '3"" of rain'
PowerShell v3引入了魔术--%选项,称为停止解析符号,它通过将未解释的任何内容传递给目标程序来减轻一些痛苦,除了扩展的-stylecmd.exe环境变量引用(例如,%USERNAME%);例如:
foo.exe --% "3\" of rain" -u %USERNAME%
请注意,仅对目标程序(而不是 PowerShell as )转义嵌入式"as就足够了。\"\`"
但是,这种方法:
- 不允许转义
%字符以避免环境变量扩展。
- 禁止直接使用 PowerShell 变量和表达式;相反,命令行必须在第一步中构建在字符串变量中,然后
Invoke-Expression在第二步中调用 with。
解决此问题的另一种解决方法* 是使用包含整个命令行cmd /c的单个参数调用 via :
cmd /c "foo.exe `"3\`" of rain`" -u $env:USERNAME"
因此,尽管 PowerShell 取得了许多进步,但在调用外部程序时并没有让转义变得更容易——相反。但是,它引入了对单引号字符串的支持。
如果您不介意安装第三方模块(由我编写),该Native模块(视窗:Install-Module Nativeie
# Simply prepend 'ie' to your external-program calls.
ie foo.exe '3" of rain' -u $env:USERNAME