ChatGPT解决这个技术问题 Extra ChatGPT

在批处理脚本中转义双引号

我将如何用转义的双引号替换批处理文件参数中的所有双引号?这是我当前的批处理文件,它在字符串中扩展了它的所有命令行参数:

@echo off
call bash --verbose -c "g++-linux-4.1 %*"

然后它使用该字符串调用 Cygwin 的 bash,执行 Linux 交叉编译器。不幸的是,我将这些参数传递到我的批处理文件中:

"launch-linux-g++.bat" -ftemplate-depth-128 -O3 -finline-functions 
-Wno-inline -Wall  -DNDEBUG   -c 
-o "C:\Users\Me\Documents\Testing\SparseLib\bin\Win32\LinuxRelease\hello.o" 
"c:\Users\Me\Documents\Testing\SparseLib\SparseLib\hello.cpp"

传入的第一个路径周围的第一个引号过早地结束了传递给 GCC 的字符串,并将其余参数直接传递给 bash(这非常失败。)

我想如果我可以将参数连接成一个字符串然后转义引号它应该可以正常工作,但我很难确定如何做到这一点。有人知道吗?


E
Eclipse

批处理脚本中的转义字符是 ^。但是对于双引号字符串,双引号:

"string with an embedded "" character"

双引号对我不起作用,但 ^ 像冠军一样工作。
^ 是仅在 unquoted 字符串中的转义字符;在双引号字符串中,它被视为文字。与 Unix(类 POSIX)shell 不同,cmd.exe 不提供对双引号字符串内的双引号的标准化 shell 处理,解释留给被调用的程序(在下一条评论中继续)。
(接上一条评论)在实践中,大多数可执行文件/脚本解释器应用了期望 " 字符的 C 约定。在双引号字符串中作为 \" 转义(至少适用于:C/C++、Python、Perl、Ruby)。相比之下,"" 仅在 少数 种情况下被识别:在传递给批处理文件的参数中,"" 被识别为 嵌入 双引号,但在相应的 %<n> 参数中按原样保留,即使在使用 %~<n> 删除 封闭 双引号后也是如此。 Python优雅地将 "" 识别为 \" 的替代品。
m
mklement0

eplawless's own answer 简单有效地解决了他的具体问题:它将整个参数列表中的所有 " 实例替换为 \",这就是 Bash 需要在双引号字符串中使用双引号来表示的方式。

为了回答 如何使用 cmd.exe 在双引号字符串中转义双引号的问题,Windows 命令行解释器(无论是在命令行上 - 通常仍然被错误地称为“DOS提示” - 或在批处理文件中):查看底部以查看 PowerShell

tl;博士:

答案取决于您调用的程序:

将参数传递给(另一个)批处理文件时必须使用“”,并且可以将“”用于使用 Microsoft 的 C/C++/.NET 编译器(也接受 \")创建的应用程序,在 Windows 上包括 Python、Node. js 和 PowerShell (Core) 7+ 的 CLI (pwsh) 但不是 Windows PowerShell 的 (powershell.exe): 示例: foo.bat "We had 3"" of rain."

示例: foo.bat "We have 3"" of rain."

以下内容仅适用于目标批处理文件:“”是让命令解释器 (cmd.exe) 将整个双引号字符串视为单个参数的唯一方法(尽管如果您只是传递所有参数,这无关紧要到另一个程序,使用 %*) 然而,遗憾的是,不仅保留了封闭的双引号(像往常一样),而且双引号也保留了,因此获得预期的字符串是一个两步过程;例如,假设双引号字符串作为第一个参数传递, %1: set "str=%~1" 删除封闭的双引号; set "str=%str:""="%" 然后将双引号转换为单引号。确保在赋值部分周围使用封闭的双引号,以防止对值进行不必要的解释。

"" 是让命令解释器 (cmd.exe) 将整个双引号字符串视为单个参数的唯一方法(尽管如果您只是将所有参数传递给另一个程序,使用 %* 则无关紧要)

然而,遗憾的是,不仅保留了封闭的双引号(像往常一样),而且双引号也保留了,因此获得预期的字符串是一个两步过程;例如,假设双引号字符串作为第一个参数传递, %1: set "str=%~1" 删除封闭的双引号; set "str=%str:""="%" 然后将双引号转换为单引号。确保在赋值部分周围使用封闭的双引号,以防止对值进行不必要的解释。

set "str=%~1" 删除封闭的双引号; set "str=%str:""="%" 然后将双引号转换为单引号。确保在赋值部分周围使用封闭的双引号,以防止对值进行不必要的解释。

\" 是许多其他程序(例如,Ruby、Perl、PHP 以及使用 CommandLineToArgv Windows API 函数来解析其命令行参数的程序)所必需的 - 作为唯一选项,但它从 cmd 使用。 exe 不健壮且不安全:\" 是许多可执行文件和解释器所需要的 - 包括 Windows PowerShell - 当从外部传递字符串时,在命令行上 - 或者,在 Microsoft 的编译器的情况下,支持作为 "" 的替代方案- 不过,最终由目标程序来解析参数列表。示例: foo.exe "We had 3\" of rain." 但是,使用 \" 可能会中断调用,并且至少假设会导致不必要的、任意执行命令和/或输入/输出重定向:以下字符存在这种风险: & | < > 例如,以下导致ver命令的意外执行;请参阅下面的解释和解决方法的下一个要点: foo.exe "3\" of snow" "& ver." 对于调用 Windows PowerShell CLI,powershell.exe、\"" 和 "^"" 是强大但有限的替代方案(请参阅下面的“调用 PowerShell 的 CLI ...”部分)。

\" 是许多可执行文件和解释器所需要的——包括 Windows PowerShell——当从外部传递字符串时,在命令行上——或者,在微软的编译器的情况下,支持作为 "" 的替代品——但最终,它已经完成了到目标程序来解析参数列表。例如:foo.exe "We had 3\" of rain."

示例: foo.exe “我们有 3\” 的雨。”

但是,使用 \" 可能会中断调用,并且至少会导致不必要的任意执行命令和/或输入/输出重定向:以下字符存在这种风险:& | < > 例如,以下字符会导致意外执行ver 命令;请参阅下面的进一步解释和解决方法的下一个要点: foo.exe "3\" of snow" "& ver."

以下字符存在此风险: & | < >

例如,以下结果会导致 ver 命令的意外执行;请参阅下面的解释和解决方法的下一个要点: foo.exe "3\" of snow" "& ver."

foo.exe "3\" of snow" "& ver."

对于调用 Windows PowerShell CLI,powershell.exe、\"" 和 "^"" 是强大但有限的替代方法(请参阅下面的“调用 PowerShell 的 CLI ...”部分)。

如果您必须使用 cmd.exe 中的“\”,则 cmd.exe 中只有 3 种安全方法,但它们非常麻烦:向 TS 致敬以寻求帮助。在您的批处理中使用(可能是选择性的)延迟变量扩展文件中,您可以将文字 \" 存储在变量中,并使用 !var! 在“...”字符串中引用该变量!语法 - 请参阅 T S 的有用答案。上述方法尽管很麻烦,但其优点是您可以有条不紊地应用它,并且它可以在任何输入下稳健地工作。只有使用 LITERAL 字符串 - 不涉及 VARIABLES 的字符串 - 你会得到一个类似的有条不紊的方法:绝对 ^-escape 所有 cmd.exe 元字符:“ & | < > 和 - 如果你还想抑制变量扩展 - %: foo.exe ^ "3\^" of snow^" ^"^& ver.^" 否则,您必须根据识别字符串 cmd.exe 的哪些部分由于将 \" 误解为结束分隔符而认为未引用来制定字符串:在文字部分包含shell 元字符:^-转义它们;使用上面的示例,必须是 & ^-转义: foo.exe "3\" of snow" "^& ver."在带有 %...% 样式变量引用的部分中:确保 cmd.exe 将它们视为“...”字符串的一部分,并且变量值本身没有嵌入的不平衡引号 - 这甚至并不总是可能的.

在批处理文件中使用(可能是选择性的)延迟变量扩展,您可以将文字 \" 存储在变量中,并使用 !var! 语法在“...”字符串中引用该变量 - 请参阅 T S 的有用答案。上述方法,尽管很麻烦,但它的优点是您可以有条不紊地应用它,并且它可以在任何输入下稳健地工作。

上述方法尽管很麻烦,但其优点是您可以有条不紊地应用它,并且它可以在任何输入下稳健地工作。

只有使用 LITERAL 字符串 - 不涉及 VARIABLES 的字符串 - 你会得到一个类似的有条不紊的方法:绝对 ^-escape 所有 cmd.exe 元字符:“ & | < > 和 - 如果你还想抑制变量扩展 - %: foo.exe ^雪的"3\^"^"^"^&ver.^"

否则,您必须根据识别字符串 cmd.exe 的哪些部分由于将 \" 误解为结束分隔符而认为未引用来制定您的字符串:在包含 shell 元字符的文字部分中:^-转义它们;使用上面的示例,它是 &必须被^-转义:foo.exe "3\" of snow" "^& ver."在带有 %...% 样式变量引用的部分中:确保 cmd.exe 将它们视为“...”字符串的一部分,并且变量值本身没有嵌入的不平衡引号 - 这甚至并不总是可能的.

在包含 shell 元字符的文字部分中:^-转义它们;使用上面的例子,它是 & 必须被 ^-转义: foo.exe "3\" of snow" "^& ver."

在带有 %...% 样式变量引用的部分中:确保 cmd.exe 将它们视为“...”字符串的一部分,并且变量值本身没有嵌入的不平衡引号 - 这甚至并不总是可能的.

背景

注意:这是基于我自己的实验。如果我错了,请告诉我。

类 POSIX 的 shell,例如类 Unix 系统上的 Bash,在将参数单独传递给目标程序之前对参数列表(字符串)进行标记:在其他扩展中,它们将参数列表拆分为单个单词(分词)并从结果词(引号删除)。目标程序会收到一组单独的逐字参数,即删除了句法引号。

相比之下,Windows 命令解释器显然不会对参数列表进行标记,而只是传递包含 all 参数的 single 字符串 - 包括引用字符。 - 到目标程序。
但是,一些预处理发生在将单个字符串传递给目标程序之前:^ 转义字符。双引号之外的字符串被删除(它们转义以下字符。),变量引用(例如,%USERNAME%)首先被插值

因此,与 Unix 不同,目标程序负责解析参数字符串并将其分解为去掉引号的单个参数。因此,不同的程序可能需要不同的转义方法,并且没有单一的转义机制可以保证适用于所有程序 - https://stackoverflow.com/a/4094897/45375 包含关于 Windows 命令行解析的无政府状态的优秀背景.

实际上,\" 很常见,但从 cmd.exe 开始并不安全,如上所述:

由于 cmd.exe 本身不能将 \" 识别为 转义 双引号,因此它可能会将命令行上的后续标记误解为 未引用 并可能将它们解释为 < em>commands 和/或 输入/输出重定向
简而言之:如果以下任何字符跟随一个 开头或不平衡,问题就会出现 \"& | < >;例如:

foo.exe "3\" of snow" "& ver."

cmd.exe 看到以下标记,这是由于将 \" 误解为常规双引号引起的:

"3\"

雪” ”

休息:&版本。

由于 cmd.exe 认为 & ver. 未引用,因此它将其解释为 &(命令序列运算符),后跟要执行的命令的名称(ver. - { 5} 被忽略;ver 报告 cmd.exe 的版本信息)。
总体效果是:

首先,仅使用前 3 个标记调用 foo.exe。

然后,执行命令ver。

即使在意外命令没有伤害的情况下,您的整体命令也不会按设计工作,因为并非所有参数都传递给它。

许多编译器/解释器只识别 \" - 例如,GNU C/C++ 编译器、Perl、Ruby、PHP 以及使用 CommandLineToArgv Windows API 函数来解析其命令的程序 -行参数 - 对于他们这个问题没有简单的解决方案。
本质上,您必须提前知道命令行的哪些部分被误解为未引用,并有选择地{4 } - 转义这些部分中 & | < > 的所有实例。

相比之下,使用 "" 是安全的,但遗憾的是只有基于 Microsoft 编译器的可执行文件和批处理文件支持(在批处理文件的情况下,使用上面讨论的怪癖),值得注意的是不包括 PowerShell - 请参阅下一节。

从 cmd.exe 或类似 POSIX 的 shell 调用 PowerShell 的 CLI:

注意:有关如何在 PowerShell 中处理引用,请参阅底部部分。

从外部调用 - 例如,从 cmd.exe,无论是从命令行还是批处理文件:

PowerShell [Core] v6+ 现在可以正确识别“”(除了 \"),这既可以安全使用又可以保留空格。 pwsh -c " ""a & c"".length " 不会中断并正确产生6

pwsh -c " ""a & c"".length " 不会中断并正确产生 6

Windows PowerShell(最新和最终版本为 5.1 的旧版)仅识别 \" 或 """,后者是 cmd.exe 中最可靠的选择,格式为 "^"""(即使内部 PowerShell 使用 `作为双引号字符串中的转义字符,并且还接受 "" - 见底部),如下所述:

cmd.exe / 批处理文件调用 Windows PowerShell

"" 中断,因为它根本不受支持: powershell -c " ""ab c"".length " -> error "The string is missing the terminator"

powershell -c " ""ab c"".length " -> 错误 "字符串缺少终止符"

\" 和 """ 原则上工作,但不安全: powershell -c " \"ab c\".length " 按预期工作:它输出 5(注意 2 个空格)但它不安全,因为cmd.exe 元字符会中断命令,除非转义: powershell -c " \"a& c\".length " 中断,因为 & 必须转义为 ^&

powershell -c " \"ab c\".length " 按预期工作:它输出 5(注意 2 个空格)

但这并不安全,因为 cmd.exe 元字符会破坏命令,除非转义: powershell -c " \"a& c\".length " 中断,因为 & 必须转义为 ^&

\"" 是安全的,但会规范化内部空白,这可能是不希望的:

powershell -c " \""a& c\"".length " 输出 4(!),因为 2 个空格被归一化为 1。

"^"" 特别是 Windows PowerShell 的最佳选择,它既安全又保留空白,但对于 PowerShell Core(在 Windows 上),它与 \"" 相同,即空白规范化(如上所述,简单在此处使用“”)。归功于 Venryx 发现了这种方法。powershell -c““^”“a& c”^““.length” 有效:不会中断 - 尽管 & - 并且输出 5,即正确保存空白。PowerShell 核心: pwsh -c " "^""a& c"^"".length " 有效,但输出 4,即标准化空白,就像 \"" 一样。

powershell -c " "^""a& c"^"".length " 有效:不会中断 - 尽管 & - 并且输出 5,即正确保留空白。

PowerShell 核心: pwsh -c " "^""a& c"^"".length " 有效,但输出 4,即标准化空格,就像 \"" 一样。

类 Unix 平台(Linux、macOS)上,当从类似 POSIX 的 shell(例如 bash)调用 PowerShell [Core] 的 CLI pwsh 时:

必须使用 \",但它既安全又保留空白

$ pwsh -c " \"a&  c|\".length" # OK: 5

相关信息

只能用作不带引号的字符串中的转义字符 - 在双引号字符串中,^ 不特殊并被视为文字。 CAVEAT:在传递给调用语句的参数中使用 ^ 被破坏(这适用于调用的两种用法:调用另一个批处理文件或二进制文件,以及调用同一批处理文件中的子例程):双引号中的 ^ 实例莫名其妙加倍,改变传递的值:例如,如果变量 %v% 包含文字值 a^b,则调用 :foo "%v%" 将 "a^^b"(!) 分配给子程序中的 %1(第一个参数) :富。不带引号的 ^ 与 call 的使用完全被打破,因为 ^ 不能再用于转义特殊字符:例如,调用 foo.cmd a^&b 悄悄地中断(而不是传递文字 a&b 太 foo.cmd,就像没有call) - foo.cmd 甚至从未被调用过(!),至少在 Windows 7 上是这样。

CAVEAT:在传递给调用语句的参数中使用 ^ 被破坏(这适用于调用的两种用法:调用另一个批处理文件或二进制文件,以及调用同一批处理文件中的子例程):双引号中的 ^ 实例莫名其妙加倍,改变传递的值:例如,如果变量 %v% 包含文字值 a^b,则调用 :foo "%v%" 将 "a^^b"(!) 分配给子程序中的 %1(第一个参数) :富。不带引号的 ^ 与 call 的使用完全被破坏,因为 ^ 不能再用于转义特殊字符:例如,调用 foo.cmd a^&b 悄悄地中断(而不是传递文字 a&b 太 foo.cmd,就像没有call) - foo.cmd 甚至从未被调用过(!),至少在 Windows 7 上是这样。

^ 双引号值中的实例莫名其妙地加倍,改变了传递的值:例如,如果变量 %v% 包含文字值 a^b,则调用 :foo "%v%" 将 "a^^b"(!) 分配给子例程 :foo 中的 %1(第一个参数)。

不带引号的 ^ 与 call 的使用完全被破坏,因为 ^ 不能再用于转义特殊字符:例如,调用 foo.cmd a^&b 悄悄地中断(而不是传递文字 a&b 太 foo.cmd,就像没有call) - foo.cmd 甚至从未被调用过(!),至少在 Windows 7 上是这样。

不幸的是,转义文字 % 是一种特殊情况,它需要不同的语法,具体取决于字符串是在命令行还是在批处理文件中指定的;请参阅 https://stackoverflow.com/a/31420292/45375 简而言之:在批处理文件中,使用 %%。在命令行中,% 不能被转义,但如果在不带引号的字符串的开头、结尾或变量名内放置 ^(例如,echo %^foo%),则可以防止变量扩展(插值);不属于变量引用的命令行上的 % 实例被视为文字(例如,100%)。

简而言之:在批处理文件中,使用 %%。在命令行中,% 不能被转义,但如果在不带引号的字符串的开头、结尾或变量名内放置 ^(例如,echo %^foo%),则可以防止变量扩展(插值);不属于变量引用的命令行上的 % 实例被视为文字(例如,100%)。

通常,为了安全地使用可能包含空格和特殊字符的变量值: 赋值:将变量名称和值括在一对双引号中;例如, set "v=a & b" 将文字值 a & b 分配给变量 %v% (相比之下, set v="a & b" 会使双引号成为值的一部分)。将文字 % 实例转义为 %% (仅适用于批处理文件 - 见上文)。参考:双引号变量引用以确保它们的值没有被插值;例如, echo "%v%" 不会对 %v% 的值进行插值并打印 "a & b" (但请注意,双引号也总是会打印)。相比之下,echo %v% 将文字 a 传递给 echo,将 & 解释为命令排序运算符,因此尝试执行名为 b 的命令。另请注意上述警告在调用语句中重新使用 ^。外部程序通常负责删除参数周围的双引号,但是,如前所述,在批处理文件中,您必须自己做(例如, %~1 从第一个参数中删除双引号),遗憾的是,那里我知道没有直接的方法可以让 echo 在没有封闭双引号的情况下忠实地打印变量值。 Neil 提供了一个基于 for 的变通方法,只要该值没有嵌入双引号,它就可以工作;例如:设置“var=^&')|;,%!” for /f "delims=" %%v in ("%var%") do echo %%~v

赋值:将变量名和值都括在一对双引号中;例如, set "v=a & b" 将文字值 a & b 分配给变量 %v% (相比之下, set v="a & b" 会使双引号成为值的一部分)。将文字 % 实例转义为 %% (仅适用于批处理文件 - 见上文)。

参考:双引号变量引用以确保它们的值没有被插值;例如, echo "%v%" 不会对 %v% 的值进行插值并打印 "a & b" (但请注意,双引号也总是会打印)。相比之下,echo %v% 将文字 a 传递给 echo,将 & 解释为命令排序运算符,因此尝试执行名为 b 的命令。另请注意上述警告在调用语句中重新使用 ^。外部程序通常负责删除参数周围的双引号,但是,如前所述,在批处理文件中,您必须自己做(例如, %~1 从第一个参数中删除双引号),遗憾的是,那里我知道没有直接的方法可以让 echo 在没有封闭双引号的情况下忠实地打印变量值。 Neil 提供了一个基于 for 的变通方法,只要该值没有嵌入双引号,它就可以工作;例如:设置“var=^&')|;,%!” for /f "delims=" %%v in ("%var%") do echo %%~v

外部程序通常负责删除参数周围的双引号,但是,如前所述,在批处理文件中,您必须自己做(例如, %~1 从第一个参数中删除双引号),遗憾的是,那里我知道没有直接的方法可以让 echo 在没有封闭双引号的情况下忠实地打印变量值。 Neil 提供了一个基于 for 的变通方法,只要该值没有嵌入双引号,它就可以工作;例如:设置“var=^&')|;,%!” for /f "delims=" %%v in ("%var%") do echo %%~v

Neil 提供了一个基于 for 的变通方法,只要该值没有嵌入双引号,它就可以工作;例如:设置“var=^&')|;,%!” for /f "delims=" %%v in ("%var%") do echo %%~v

cmd.exe 不将单引号识别为字符串分隔符 ('...') - 它们被视为文字,通常不能用于分隔带有嵌入空格的字符串;此外,紧靠单引号的标记和介于两者之间的任何标记都被 cmd.exe 视为未引用并进行相应解释。然而,鉴于目标程序最终会执行自己的参数解析,一些程序(例如 Ruby)即使在 Windows 上也能识别单引号字符串。相比之下,C/C++ 可执行文件和 Perl 无法识别它们。但是,即使目标程序支持,也不建议使用单引号字符串,因为它们的内容不受 cmd.exe 潜在有害解释的保护。

然而,鉴于目标程序最终会执行自己的参数解析,一些程序(例如 Ruby)即使在 Windows 上也能识别单引号字符串。相比之下,C/C++ 可执行文件和 Perl 无法识别它们。但是,即使目标程序支持,也不建议使用单引号字符串,因为它们的内容不受 cmd.exe 潜在有害解释的保护。

从 PowerShell 中引用:

Windows PowerShell 是一个比 cmd.exe 高级得多的 shell,它多年来一直是 Windows 的一部分(并且 PowerShell Core 也将 PowerShell 体验带入了 macOS 和 Linux)。

PowerShell 在引用方面始终在内部工作:

在双引号字符串中,使用 `" 或 "" 转义双引号

在单引号字符串中,使用 '' 转义单引号

这适用于 PowerShell 命令行以及从 PowerShell 中将参数传递给 PowerShell 脚本或函数时。

(如上所述,从外部将转义的双引号传递给 PowerShell 需要 \",或者更可靠的是 \"" - 没有其他方法)。

遗憾的是,当从 PowerShell 调用外部程序时,您既需要适应 PowerShell 自己的引用规则,又需要为目标程序转义:

这个有问题的行为也在这个答案中进行了讨论和总结; PowerShell Core 7.2.0-preview.5 中引入的实验性 PSNativeCommandArgumentPassing 功能 - 假设它成为官方功能 - 将至少为那些接受 \" 的外部程序解决这个问题。

双引号字符串中的双引号:

考虑字符串 "3`" of rain",它在 PowerShell 内部转换为文字 3" of rain

如果您想将此字符串传递给外部程序,您必须将目标程序的转义 应用到 PowerShell 的;假设您想将字符串传递给 C 程序,该程序期望嵌入的双引号被转义为 \"

foo.exe "3\`" of rain"

注意 both `" - 让 PowerShell 快乐 - \ - 让目标程序快乐 - 必须存在。

相同的逻辑适用于调用批处理文件,其中必须使用 ""

foo.bat "3`"`" of rain"

相比之下,在双引号字符串中嵌入单引号根本不需要转义。

-引号内的-引号字符串需要额外转义;考虑 '2'' of snow',它是 2' of snow 的 PowerShell 表示。

foo.exe '2'' of snow'
foo.bat '2'' of snow'

PowerShell 在将单引号字符串传递给目标程序之前将其转换为双引号字符串。

但是,单引号字符串中的双引号(对于 PowerShell 不需要转义)对于目标程序仍然需要转义:

foo.exe '3\" of rain'
foo.bat '3"" of rain'

PowerShell v3 引入了神奇的 --% 选项,称为 stop-parsing symbol,它通过在其后传递任何内容来减轻一些痛苦未解释到目标程序,除了cmd.exe风格的环境变量引用(例如,%USERNAME%),它们被扩展了;例如:

foo.exe --% "3\" of rain" -u %USERNAME%

请注意,仅为目标程序将嵌入的 " 转义为 \"(对于 PowerShell 来说也不是 \`")就足够了。

但是,这种方法:

不允许转义 % 字符以避免环境变量扩展。

禁止直接使用 PowerShell 变量和表达式;相反,命令行必须在第一步中构建在字符串变量中,然后在第二步中使用 Invoke-Expression 调用。

解决此问题的替代解决方法* 是通过 cmd /c 使用包含整个命令行的单个参数进行调用:

cmd /c "foo.exe `"3\`" of rain`" -u $env:USERNAME"

因此,尽管 PowerShell 取得了许多进步,但在调用外部程序时并没有让转义变得更容易——相反。但是,它引入了对单引号字符串的支持。

如果您不介意安装第三方模块(由我编写),Native module (Install-Module Native) 提供了向后和向前兼容的辅助函数 ie,它无需额外的转义并包含Windows 上备受瞩目的 CLI 的重要调整:

# Simply prepend 'ie' to your external-program calls.
ie foo.exe '3" of rain' -u $env:USERNAME

很好的描述!但是... Literal use of ^ in double-quoted strings can, be problematic when applying ~ ... 不是真的。波浪号仅在存在时删除外部引号。脱掉插入符号是处理本身的问题。通常 set "param=%~1" 可以解决这个问题。
谢谢@jeb,这个问题确实不是特定于使用 ~ - 我已经更新了我的答案以反映我的新理解 - 如果您发现问题,请发表评论。您对将参数传递给 call 语句时自动发生的 ^ 加倍有解释吗?
我只能猜测 MS 的某个人认为它很棒。在第二个解析阶段将自动删除双引号。但这是一个很大的失败,因为它在引号中不起作用,并且它有效地防止了任何特殊字符的转义。像 call echo cat^^&dog 一样,仅靠任何数量的插入符号都无法解决
谢谢,@jeb,我什至没有考虑过将 ^call 一起使用的未引用,正如您所指出的,它被严重破坏了。似乎在 call echo cat^&dog(正确转义 & 的单个 ^)中,甚至从未调用目标命令(echo)(!) - 整个命令安静地失败。我已经相应地更新了答案。
在所有这些谈话中,有人应该提到cmd.exe实际上遵循的简单规则,以螺旋式进入所有这些复杂性:因为命令行是从 left-to-right< /b>,双引号状态只是翻转。如果左侧有 odd",则您在令牌中,并且分隔符被禁用;如果向左有 偶数",则启用分隔符。这就是您可以取消最后一个 " 的原因。并且 "" 没有定界,因为虽然它最终不会改变奇/偶状态,但是内部没有定界符。
e
eplawless

谷歌最终想出了答案。批量替换字符串的语法是这样的:

set v_myvar=replace me
set v_myvar=%v_myvar:ace=icate%

这会产生“复制我”。我的脚本现在看起来像这样:

@echo off
set v_params=%*
set v_params=%v_params:"=\"%
call bash -c "g++-linux-4.1 %v_params%"

它将 " 的所有实例替换为 \",为 bash 正确转义。


T
T S

作为 mklement0's excellent answer 的补充:

几乎所有可执行文件都接受 \" 作为转义的 "。然而,在 cmd 中的安全使用几乎只有使用 DELAYEDEXPANSION 才能实现。
要将文字 " 显式发送到某个进程,请将 \" 分配给环境变量,然后在需要传递引号时使用该变量。例子:

SETLOCAL ENABLEDELAYEDEXPANSION
set q=\"
child "malicious argument!q!&whoami"

注意 SETLOCAL ENABLEDELAYEDEXPANSION 似乎只适用于批处理文件。要在交互式会话中获得 DELAYEDEXPANSION,请启动 cmd /V:ON

如果您的批处理文件不适用于 DELAYEDEXPANSION,您可以暂时启用它:

::region without DELAYEDEXPANSION

SETLOCAL ENABLEDELAYEDEXPANSION
::region with DELAYEDEXPANSION
set q=\"
echoarg.exe "ab !q! & echo danger"
ENDLOCAL

::region without DELAYEDEXPANSION

如果您想从包含转义为 "" 的引号的变量中传递动态内容,您可以在展开时将 "" 替换为 \"

SETLOCAL ENABLEDELAYEDEXPANSION
foo.exe "danger & bar=region with !dynamic_content:""=\"! & danger"
ENDLOCAL

这种替换对于 %...% 样式扩展是不安全的!

如果是 OP bash -c "g++-linux-4.1 !v_params:"=\"!" 是安全版本。

如果出于某种原因甚至暂时无法启用 DELAYEDEXPANSION,请继续阅读:

如果总是需要转义特殊字符,而不是有时需要转义,则在 cmd 中使用 \" 会更安全一些。 (如果它是一致的,它不太可能忘记插入符号......)

为了实现这一点,在任何引用之前都带有一个插入符号 (^"),应该作为文字到达子进程的引用必须另外使用反斜杠 (\^") 进行转义。 ALL shell 元字符也必须使用 ^ 进行转义,例如 & => ^&; | => ^|; > => ^>;等等

例子:

child ^"malicious argument\^"^&whoami^"

来源:Everyone quotes command line arguments the wrong way,参见“更好的引用方法”

要传递动态内容,需要确保以下内容:
包含变量的命令部分必须被 cmd.exe 视为“引用”(如果变量可以包含引号,则这是不可能的 - don' t 写 %var:""=\"%)。为此,变量之前的最后一个 " 和变量之后的第一个 " 不是 ^ 转义的。这两个 " 之间的 cmd 元字符不得转义。例子:

foo.exe ^"danger ^& bar=\"region with %dynamic_content% & danger\"^"

如果 %dynamic_content% 可以包含不匹配的引号,这是不安全的。


明白了,谢谢。是的,明确地^-转义 all 元字符工作得很好,并且可以更有条理地应用(但它显然是您选择的身体部位的一种皇家痛苦)。我已经相应地更新了我的答案(并给了你信用)。
@mklement0 谢谢!是的,这确实是一种痛苦。这很烦人,而且仍然很容易忘记元字符(因此我主要使用 !q! 方式)。注意:上次编辑后,您的回答略有不一致:在顶部附近您说:“不要使用^"”。稍后您使用 ^" 作为解决方法的一部分。也许你可以解释两种方式? (1) 转义所有元字符(更有条理)/ (2) 有选择地转义“未引用”区域中的元字符(有时需要传递动态内容,例如 foo.exe ^"danger ^& bar=\"%dynamic_content%\"^" - 这样为 cmd 引用变量)
好点,谢谢 - 答案已更新。我还更清楚地说明了 MS 编译器同时接受 \"""。我已将您的答案链接到更复杂的基于变量的方法。让我知道现在是否有意义。
@mklement0 你的回答总是有道理的 :-) 我只是想为可能的改进提供一些想法。我还在我的答案中添加了关于 %dynamic_content% 的示例。您认为它足够详细还是我需要解释更多?
酷,谢谢你让我知道。将 setlocal delayedexpansion 本地化是个好主意,但您应该以 endlocal(无参数)结束该块。老实说,看着你的要点,我的头开始旋转。我们真的在这里处理边缘情况,我认为未来的读者会在我们的两个答案之间找到他们需要的一切。
A
Asjad Azeem

如果字符串已经在引号内,则使用另一个引号来取消其操作。

echo "Insert tablename(col1) Values('""val1""')" 

M
Marcelo

在 Windows 10 21H1。

如果我想从批处理 (.bat) 文件运行 Everything 应用程序,我在双引号参数中使用 """

"C:\Program Files\Everything\Everything.exe" -search "<"""D:\My spaced folder""" | """Z:\My_non_spaced_folder"""> <*.jpg | *.jpeg | *.avi | *.mp4>"

希望能帮助到你。