eplawless 自己的回答简单有效地解决了他的具体问题:它将"
整个参数列表中的所有实例替换为\"
,这就是 Bash 需要在双引号字符串中使用双引号来表示的方式。
要普遍回答如何使用Windows 命令行解释器在双引号字符串中转义双引号的问题(无论是在命令行上 - 通常仍被错误地称为“DOS 提示符” - 还是在批处理文件中):cmd.exe
请参阅底部以了解PowerShell。
tl;博士:
答案取决于您调用的程序:
将参数传递给(另一个)批处理文件时必须使用""
,并且可以与使用Microsoft的 C/C++/.NET 编译器(也接受)创建的应用程序一起使用,在 Windows 上包括 Python、Node.js和PowerShell (核心)7+ 的CLI()但不是 Windows PowerShell 的():""
\"
pwsh
powershell.exe
- 例子:
foo.bat "We had 3"" of rain."
以下内容仅适用于定位批处理文件:
\"
是许多其他程序(例如,Ruby、Perl、PHP 以及使用 CommandLineToArgv
Windows 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,以及使用 CommandLineToArgv
Windows 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 Native
ie
# Simply prepend 'ie' to your external-program calls.
ie foo.exe '3" of rain' -u $env:USERNAME