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

发布于 2024-07-13 15:57:24 字数 625 浏览 4 评论 0原文

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

@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 (这明显失败了) .)

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

How would I go about replacing all of the double quotes in my batch file's parameters with escaped double quotes? This is my current batch file, which expands all of its command line parameters inside the string:

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

It then uses that string to make a call to Cygwin's bash, executing a Linux cross-compiler. Unfortunately, I'm getting parameters like these passed in to my batch file:

"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"

Where the first quote around the first path passed in is prematurely ending the string being passed to GCC, and passing the rest of the parameters directly to bash (which fails spectacularly.)

I imagine if I can concatenate the parameters into a single string then escape the quotes it should work fine, but I'm having difficulty determining how to do this. Does anyone know?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

旧城空念 2024-07-20 15:57:24

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

"string with an embedded "" character"

The escape character in batch scripts is ^. But for double-quoted strings, double up the quotes:

"string with an embedded "" character"
栖迟 2024-07-20 15:57:24

eplawless自己的答案简单而有效地解决了他的具体问题:它取代了所有整个参数列表中的 " 实例带有 \",这就是 Bash 要求在双引号字符串中使用双引号的方式。

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

tl;dr

答案取决于您正在调用哪个程序

  • 必须使用"" 将参数传递给(另一个)批处理文件并且您可以使用"" 与使用 Microsoft 的 C/C++/.NET 编译器接受 \")创建的应用程序,其中Windows 上包括 Python、Node.jsPowerShell (Core) 7+ 的 CLI (pwsh),但不是 Windows PowerShell 的 (powershell.exe):

    • 示例:foo.bat“我们下了 3 场雨。”

  • 以下内容仅适用于定位批处理文件

    • "" 是让命令解释器 (cmd.exe) 将整个双引号字符串视为单个的方法 参数(尽管如果您只是使用 %*所有 参数通过 传递给另一个程序,这并不重要) p>

    • 然而,遗憾的是,不仅保留了封闭的双引号(像往常一样),而且双转义的双引号也被保留,因此获取预期的字符串是一个两步过程; 例如,假设双引号字符串作为第一个参数传递,%1:

      • set "str=%~1" 删除括起来的双引号; set "str=%str:""="%" 然后将双双引号转换为单引号。
        请务必在赋值部分使用双引号,以防止对值进行不必要的解释。
  • \" 是许多其他程序必需的 - 作为唯一选项,(例如 Ruby 、Perl、PHP 以及使用 CommandLineToArgv Windows API 函数来解析其命令行参数),但是它在 cmd.exe 中的使用不可靠且不安全

    • \" 是许多可执行文件和解释器需要的 - 包括 Windows PowerShell - 在命令行上从外部传递字符串时- 或者,对于微软的编译器,支持作为""的替代方案 - 但最终,这取决于目标程序解析参数列表。
      • 示例:foo.exe“我们下了 3 场雨。”
    • 但是,使用 \" 可能会中断调用,并且至少在假设上会导致命令和/或输入/输出重定向的不需要的任意执行:
      • 以下字符存在此风险:& | < >
      • 例如,以下内容会导致 ver 命令意外执行; 请参阅下面的进一步解释和解决方法的下一个要点:
        • foo.exe“3\”雪“”& 版本。”

    • 用于调用Windows PowerShell CLIpowershell.exe\"""^"" 很强大,但是有限的替代方案(请参阅下面的“调用 PowerShell 的 CLI ...”部分)。


  • 如果您必须使用 cmd.exe 中的 \",则 cmd 中只有 3 种安全方法。 exe,但是相当麻烦T S 感谢他的帮助。

    • 在批处理文件中使用(可能选择性)延迟变量扩展,您可以将文字 \" 存储在变量,并使用 !var! 语法"..." 字符串中引用该变量 - 请参阅 T S 的有用答案

      • 尽管上述方法很麻烦,但其优点是您可以有条不紊地应用它,而且无论输入什么,它都稳健
    • 仅对于文字字符串(不涉及变量的字符串),您是否会得到类似的有条理的方法:明确 ^-escape all cmd .exe 元字符: " & | < > 以及 - 如果您还想抑制变量扩展 - %:
      foo.exe ^"3\^" 雪^" ^"^& 版本^”

    • 否则,您必须根据识别字符串 cmd.exe 的哪些部分考虑未加引号来制定字符串,因为\ " 作为结束分隔符:

      • 在包含 shell 元字符的文字部分中:^-转义它们; 使用上面的示例,& 必须是 ^ 转义的:
        foo.exe“3\”雪“”^& 版本。”

      • 在带有 %...% 样式变量引用的部分中:确保 cmd.exe 将它们视为"..." 字符串并且表明变量值本身没有嵌入的、不平衡的引号 - 这甚至并不总是可能的。< /p>


背景

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

类 Unix 系统上的 Bash 之类的 POSIX shell单独传递参数之前标记参数列表(字符串) > 到目标程序:除其他扩展外,它们将参数列表拆分为单个单词(单词拆分)并从结果单词中删除引号字符(引号删除)。 向目标程序提供一个数组,其中包含各个逐字参数,即删除了语法引号< /强>。

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

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

实际上,\" 很常见,但不是cmd.exe 的安全性,如上所述:

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

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

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

  • "3 \"
  • 雪" "
  • 休息: & ver.

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

  • 首先,仅使用前 3 标记调用 foo.exe
  • 然后,执行命令ver

即使在意外命令没有造成任何损害的情况下,考虑到并非所有参数都传递给它,整个命令也不会按设计工作。

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

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


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

注意:请参阅底部部分,了解 PowerShell 内部如何处理引用。

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

  • PowerShell [核心] v6+ 现在可以正确识别 ""(除了 \"),这既使用安全且保留空格< /strong>.

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

  • Windows PowerShell(旧版本,其最新版本最终版本为 5.1)仅识别 \"""",后者是 cmd.exe最可靠的选择,格式为 "^"""(甚至尽管 PowerShell 在内部使用 ` 作为双引号字符串中的转义字符,并且也接受 "" - 请参阅底部部分),如下所述:

cmd.exe< 调用 Windows PowerShell /code> / 批处理文件:

  • "" 中断,因为它根本不受支持:

    • powershell -c " ""ab c"".length " -> 错误“字符串缺少终止符”
  • \"""" 原则上可以工作,但不是安全

    • powershell -c " \"ab c\".length " 按预期工作:它输出 5 (注意 2 空格)
    • 但这并不安全,因为 cmd.exe 元字符会破坏命令,除非转义:
      powershell -c“\”a& c\".length " 中断,由于 &,必须转义为 ^&< /里>
  • < p>\""安全,但规范化内部空白,这可能是不需要的:

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

  • “^””Windows PowerShell 的最佳选择,特别是 归功于 Venryx 用于发现此方法。"" 用于 PowerShell(核心)7+

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

    • PowerShell 核心pwsh -c """a&c"".length "

    • 请参阅此答案了解更多信息。

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

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

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

# Alternative, with '...' quoting: no escaping of " needed.
$ pwsh -c ' "a&  c".length ' # OK: 5

相关信息

  • ^只能用作不带引号的字符串中的转义字符 - 在双引号字符串内, ^ 并不特殊,被视为文字。

    • 警告在传递给 call 语句的参数中使用 ^ 已被破坏(这适用于call 的用途:调用另一个批处理文件或二进制文件,并在同一批处理文件中调用子例程):
      • ^ 双引号值中的实例莫名其妙地加倍,改变了传递的值:例如,如果变量 %v% 包含文字值 a^b,则 call :foo "%v%" 赋值 "a^^b "(!) 到子例程 :foo 中的 %1(第一个参数)。
      • 未加引号的使用 ^call 的情况完全被破坏^< /code> 不能再用于转义特殊字符:例如,call foo.cmd a^&b 会悄悄中断(而不是传递文字 a&b code> 也是 foo.cmd,就像没有 call 的情况一样) - foo.cmd 甚至从未被调用(!),至少在 Windows 7 上。




  • 转义文字 % 是一种特殊情况,不幸的是,它需要不同的语法,具体取决于是否在命令行上指定了字符串。 与在批处理文件内; 请参阅https://stackoverflow.com/a/31420292/45375

    • 其缺点:在批处理文件中,使用 %%。 在命令行上,% 无法转义,但如果您将 ^ 放在不带引号的变量名称的开头、结尾或内部 字符串(例如,echo %^foo%),可以防止变量扩展(插值); 命令行上不属于变量引用的 % 实例将被视为文字(例如,100%)。
  • 通常,安全地使用可能包含空格和特殊字符的变量值

    • 赋值将变量名和值括在一对双引号中; 例如,set "v=a & b" 分配文字值a & b 到变量 %v% (相比之下,set v="a & b" 将使双引号成为值的一部分)。 将文字 % 实例转义为 %%(仅适用于批处理文件 - 参见上文)。
    • 引用双引号变量引用以确保其值不会被插值; 例如,echo“%v%”不将%v%的价值插入插值和打印“ a&amp; b”(但是请注意,双重报价也总是打印出来的)。 相比之下,echo%v%将文字传递给 echo,将&amp;解释为命令序列操作员,因此试图执行一个名为b的命令。
      还请注意,上面的警告使用^呼叫语句使用。

      • 外部程序通常会照顾删除围绕参数的双重引用,但是,如前所述,在批处理文件中,您必须自己执行(例如,%〜1要从第一个参数中删除封闭的双重报价),可悲的是,我没有直接的方法来获取echo可以忠实地打印一个无需的变量值em>封闭双引号


        • neil 提供 基于基于的方法只要值没有嵌入式双引号 ; 例如:
          设置“ var =^&amp;')|;,%!” for/f“ delims =” %% v in(“%var%”)do echo %% 〜v






  • cmd.exe do not not < /em>识别 single - 引用作为字符串定界符('...') - 它们被视为文字,通常不能用来用与嵌入式空格; 同样,因此,符合单引号的令牌和两者之间的任何令牌都被视为cmd.exe

  • 然而,鉴于目标程序最终执行自己的论点解析,诸如Ruby之类的程序甚至在Windows上也可以识别单字符串; 相比之下,c/c ++可执行文件和perl do 识别它们。
    但是,即使受到目标程序的支持,也不建议使用单引号的字符串,因为它们的内容不受cmd.exe


引用 powerShell中的引用:

windows powershell 比更先进cmd.exe,并且已经成为Windows的一部分已经多年了(和 PowerShell core 也将Powershell体验带到MacOS和Linux)。

PowerShell

  • 在引号方面
  • 始终 在内部工作引用字符串,使用'''逃脱单引号,

这在powershell命令行上起作用,以及将参数传递给powershell脚本或从 powershell中的中的函数

(如上所述) 。从外部将逃脱的双引号传递给PowerShell 需要\“”或更稳健的,\“” - 没有其他作用)。

可悲的是,当powershell的外部程序调用外部时,您将面临需要容纳PowerShell自己的引用规则逃脱 target < /em>程序:

​ 为降雨的文字3“

- 在内部转换 PowerShell的说您想将字符串传递给C程序,该程序期望将嵌入的双引号逃脱为\“

foo.exe "3\`" of rain"

请注意 >`' - 要使PowerShell快乐 - \ - 要使目标程序感到高兴 - 必须存在。

同样的逻辑也适用于调用批处理文件,其中必须使用:

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

相反,在a double> double> double>中嵌入 Quotes > - 引用的字符串根本不需要逃脱。

- 引用 引用的字符串 do not requient requem extry eveping; 考虑'2''snow',这是Snow 的2'的表示。

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

Powershell将单引号的字符串转换为双引号,然后将其传递给目标程序。

但是, double - 引用引用的字符串,不需要逃脱 powershell ,仍然需要要逃脱目标程序

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

PowerShell v3 引入了魔术 - %选项 option ,称为停止放置符号,通过将其在 未解释的之后传递到目标程序之后,减轻了一些痛苦 - 变量参考(例如%用户名%),已扩展; 例如:

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

请注意,仅针对目标程序的> “ ”如何逃脱仅适用于目标程序(而不是powerShell作为\`“”就足够了 。

但是

  • ​表达式;命令行必须在第一步中构建字符串变量,然后用invoke-expression

替代* 。这个问题是通过cmd/c与包含 整个命令行的单个参数进行调用

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

因此,尽管有许多进步,但PowerShell在调用外部程序 - 但是,

如果您不介意安装第三方模块(由我创建),则 引入了对单字符串的支持。 ://github.com/mklement0/native“ rel =“ noreferrer”> 本机 module (install-module-module)提供后向后和前向兼容助手功能IE,它消除了对额外逃逸的需求,并包含Windows上备受瞩目的CLI的重要住宿:

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

eplawless's own answer simply and effectively solves his specific problem: it replaces all " instances in the entire argument list with \", which is how Bash requires double-quotes inside a double-quoted string to be represented.

To generally answer the question of how to escape double-quotes inside a double-quoted string using cmd.exe, the Windows command-line interpreter (whether on the command line - often still mistakenly called the "DOS prompt" - or in a batch file):See bottom for a look at PowerShell.

tl;dr:

The answer depends on which program you're calling:

  • You must use "" when passing an argument to a(nother) batch file and you may use "" with applications created with Microsoft's C/C++/.NET compilers (which also accept \"), which on Windows includes Python, Node.js, and PowerShell (Core) 7+'s CLI (pwsh) but not Windows PowerShell's (powershell.exe):

    • Example: foo.bat "We had 3"" of rain."
  • The following applies to targeting batch files only:

    • "" is the only way to get the command interpreter (cmd.exe) to treat the whole double-quoted string as a single argument (though that won't matter if you simply pass all arguments through to another program, with %*)

    • Sadly, however, not only are the enclosing double-quotes retained (as usual), but so are the doubled escaped ones, so obtaining the intended string is a two-step process; e.g., assuming that the double-quoted string is passed as the 1st argument, %1:

      • set "str=%~1" removes the enclosing double-quotes; set "str=%str:""="%" then converts the doubled double-quotes to single ones.
        Be sure to use the enclosing double-quotes around the assignment parts to prevent unwanted interpretation of the values.
  • \" is required - as the only option - by many other programs, (e.g., Ruby, Perl, PHP, as well as programs that use the CommandLineToArgv Windows API function to parse their command-line arguments), but it use from cmd.exe is not robust and safe:

    • \" is what many executables and interpreters either require - including Windows PowerShell - when passed strings from the outside, on the command line - or, in the case of Microsoft's compilers, support as an alternative to "" - ultimately, though, it's up to the target program to parse the argument list.
      • Example: foo.exe "We had 3\" of rain."
    • However, use of \" can break calls and at least hypothetically result in unwanted, arbitrary execution of commands and/or input/output redirections:
      • The following characters present this risk: & | < >
      • For instance, the following results in unintended execution of the ver command; see further below for an explanation and the next bullet point for a workaround:
        • foo.exe "3\" of snow" "& ver."
    • For calling the Windows PowerShell CLI, powershell.exe, \"" and "^"" are robust, but limited alternatives (see section "Calling PowerShell's CLI ..." below).
  • If you must use \" from cmd.exe, there are only 3 safe approaches from cmd.exe, which are, however quite cumbersome: Tip of the hat to T S for his help.

    • Using (possibly selective) delayed variable expansion in your batch file, you can store literal \" in a variable and reference that variable inside a "..." string using !var! syntax - see T S's helpful answer.

      • The above approach, despite being cumbersome, has the advantage that you can apply it methodically and that it works robustly, with any input.
    • Only with LITERAL strings - ones NOT involving VARIABLES - do you get a similarly methodical approach: categorically ^-escape all cmd.exe metacharacters: " & | < > and - if you also want to suppress variable expansion - %:
      foo.exe ^"3\^" of snow^" ^"^& ver.^"

    • Otherwise, you must formulate your string based on recognizing which portions of the string cmd.exe considers unquoted due to misinterpreting \" as closing delimiters:

      • in literal portions containing shell metacharacters: ^-escape them; using the example above, it is & that must be ^-escaped:
        foo.exe "3\" of snow" "^& ver."

      • in portions with %...%-style variable references: ensure that cmd.exe considers them part of a "..." string and that that the variable values do not themselves have embedded, unbalanced quotes - which is not even always possible.


Background

Note: This is based on my own experiments. Do let me know if I'm wrong.

POSIX-like shells such as Bash on Unix-like systems tokenize the argument list (string) before passing arguments individually to the target program: among other expansions, they split the argument list into individual words (word splitting) and remove quoting characters from the resulting words (quote removal). The target program is handed an array of individual, verbatim arguments, i.e. with syntactic quotes removed.

By contrast, the Windows command interpreter apparently does not tokenize the argument list and simply passes the single string comprising all arguments - including quoting chars. - to the target program.
However, some preprocessing takes place before the single string is passed to the target program: ^ escape chars. outside of double-quoted strings are removed (they escape the following char.), and variable references (e.g., %USERNAME%) are interpolated first.

Thus, unlike in Unix, it is the target program's responsibility to parse to parse the arguments string and break it down into individual arguments with quotes removed.
Thus, different programs can require differing escaping methods and there's no single escaping mechanism that is guaranteed to work with all programs - https://stackoverflow.com/a/4094897/45375 contains excellent background on the anarchy that is Windows command-line parsing.

In practice, \" is very common, but NOT SAFE from cmd.exe, as mentioned above:

Since cmd.exe itself doesn't recognize \" as an escaped double-quote, it can misconstrue later tokens on the command line as unquoted and potentially interpret them as commands and/or input/output redirections.
In a nutshell: the problem surfaces, if any of the following characters follow an opening or unbalanced \": & | < >; for example:

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

cmd.exe sees the following tokens, resulting from misinterpreting \" as a regular double-quote:

  • "3\"
  • of
  • snow" "
  • rest: & ver.

Since cmd.exe thinks that & ver. is unquoted, it interprets it as & (the command-sequencing operator), followed by the name of a command to execute (ver. - the . is ignored; ver reports cmd.exe's version information).
The overall effect is:

  • First, foo.exe is invoked with the first 3 tokens only.
  • Then, command ver is executed.

Even in cases where the accidental command does no harm, your overall command won't work as designed, given that not all arguments are passed to it.

Many compilers / interpreters recognize ONLY \" - e.g., the GNU C/C++ compiler, Perl, Ruby, PHP, as well as programs that use the CommandLineToArgv Windows API function to parse their command-line arguments - and for them there is no simple solution to this problem.
Essentially, you'd have to know in advance which portions of your command line are misinterpreted as unquoted, and selectively ^-escape all instances of & | < > in those portions.

By contrast, use of "" is SAFE, but is regrettably only supported by Microsoft-compiler-based executables and batch files (in the case of batch files, with the quirks discussed above), which notable excludes PowerShell - see next section.


Calling PowerShell's CLI from cmd.exe or POSIX-like shells:

Note: See the bottom section for how quoting is handled inside PowerShell.

When invoked from the outside - e.g., from cmd.exe, whether from the command line or a batch file:

  • PowerShell [Core] v6+ now properly recognizes "" (in addition to \"), which is both safe to use and whitespace-preserving.

    • pwsh -c " ""a & c"".length " doesn't break and correctly yields 6
  • Windows PowerShell (the legacy edition whose latest and final version is 5.1) recognizes only \" or """, the latter being the most robust choice from cmd.exe, in the form "^""" (even though internally PowerShell uses ` as the escape character in double-quoted strings and also accepts "" - see bottom section), as discussed next:

Calling Windows PowerShell from cmd.exe / a batch file:

  • "" breaks, because it is fundamentally unsupported:

    • powershell -c " ""ab c"".length " -> error "The string is missing the terminator"
  • \" and """ work in principle, but aren't safe:

    • powershell -c " \"ab c\".length " works as intended: it outputs 5 (note the 2 spaces)
    • But it isn't safe, because cmd.exe metacharacters break the command, unless escaped:
      powershell -c " \"a& c\".length " breaks, due to the &, which would have to be escaped as ^&
  • \"" is safe, but normalizes interior whitespace, which can be undesired:

  • powershell -c " \""a& c\"".length " outputs 4(!), because the 2 spaces are normalized to 1.

  • "^"" is the best choice for Windows PowerShell, specifically Credit goes to Venryx for discovering this approach. and "" for PowerShell (Core) 7+:

    • Windows PowerShell: powershell -c " "^""a& c"^"".length " works: doesn't break - despite & - and outputs 5, i.e., correctly preserved whitespace.

    • PowerShell Core: pwsh -c """a& c"".length "

    • See this answer for more information.

On Unix-like platforms (Linux, macOS), when calling PowerShell [Core]'s CLI, pwsh, from a POSIX-like shell such as bash:

You must use \", which, however is both safe and whitespace-preserving:

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

# Alternative, with '...' quoting: no escaping of " needed.
$ pwsh -c ' "a&  c".length ' # OK: 5

Related information

  • ^ can only be used as the escape character in unquoted strings - inside double-quoted strings, ^ is not special and treated as a literal.

    • CAVEAT: Use of ^ in parameters passed to the call statement is broken (this applies to both uses of call: invoking another batch file or binary, and calling a subroutine in the same batch file):
      • ^ instances in double-quoted values are inexplicably doubled, altering the value being passed: e.g., if variable %v% contains literal value a^b, call :foo "%v%" assigns "a^^b"(!) to %1 (the first parameter) in subroutine :foo.
      • Unquoted use of ^ with call is broken altogether in that ^ can no longer be used to escape special characters: e.g., call foo.cmd a^&b quietly breaks (instead of passing literal a&b too foo.cmd, as would be the case without call) - foo.cmd is never even invoked(!), at least on Windows 7.
  • Escaping a literal % is a special case, unfortunately, which requires distinct syntax depending on whether a string is specified on the command line vs. inside a batch file; see https://stackoverflow.com/a/31420292/45375

    • The short of it: Inside a batch file, use %%. On the command line, % cannot be escaped, but if you place a ^ at the start, end, or inside a variable name in an unquoted string (e.g., echo %^foo%), you can prevent variable expansion (interpolation); % instances on the command line that are not part of a variable reference are treated as literals (e.g, 100%).
  • Generally, to safely work with variable values that may contain spaces and special characters:

    • Assignment: Enclose both the variable name and the value in a single pair of double-quotes; e.g., set "v=a & b" assigns literal value a & b to variable %v% (by contrast, set v="a & b" would make the double-quotes part of the value). Escape literal % instances as %% (works only in batch files - see above).
    • Reference: Double-quote variable references to make sure their value is not interpolated; e.g., echo "%v%" does not subject the value of %v% to interpolation and prints "a & b" (but note that the double-quotes are invariably printed too). By contrast, echo %v% passes literal a to echo, interprets & as the command-sequencing operator, and therefore tries to execute a command named b.
      Also note the above caveat re use of ^ with the call statement.

      • External programs typically take care of removing enclosing double-quotes around parameters, but, as noted, in batch files you have to do it yourself (e.g., %~1 to remove enclosing double-quotes from the 1st parameter) and, sadly, there is no direct way that I know of to get echo to print a variable value faithfully without the enclosing double-quotes.
        • Neil offers a for-based workaround that works as long as the value has no embedded double quotes; e.g.:
          set "var=^&')|;,%!" for /f "delims=" %%v in ("%var%") do echo %%~v
  • cmd.exe does not recognize single-quotes as string delimiters ('...') - they are treated as literals and cannot generally be used to delimit strings with embedded whitespace; also, it follows that the tokens abutting the single-quotes and any tokens in between are treated as unquoted by cmd.exe and interpreted accordingly.

    • However, given that target programs ultimately perform their own argument parsing, some programs such as Ruby do recognize single-quoted strings even on Windows; by contrast, C/C++ executables and Perl do not recognize them.
      Even if supported by the target program, however, it is not advisable to use single-quoted strings, given that their contents are not protected from potentially unwanted interpretation by cmd.exe.

Quoting from within PowerShell:

Windows PowerShell is a much more advanced shell than cmd.exe, and it has been a part of Windows for many years now (and PowerShell Core brought the PowerShell experience to macOS and Linux as well).

PowerShell works consistently internally with respect to quoting:

  • inside double-quoted strings, use `" or "" to escape double-quotes
  • inside single-quoted strings, use '' to escape single-quotes

This works on the PowerShell command line and when passing parameters to PowerShell scripts or functions from within PowerShell.

(As discussed above, passing an escaped double-quote to PowerShell from the outside requires \" or, more robustly, \"" - nothing else works).

Sadly, when invoking external programs from PowerShell, you're faced with the need to both accommodate PowerShell's own quoting rules and to escape for the target program:

  • This problematic behavior is also discussed and summarized in this answer; the experimental PSNativeCommandArgumentPassing feature introduced in PowerShell Core 7.2.0-preview.5 - assuming it becomes an official feature - will fix this at least for those external programs that accept \".

Double-quotes inside double-quoted strings:

Consider string "3`" of rain", which PowerShell-internally translates to literal 3" of rain.

If you want to pass this string to an external program, you have to apply the target program's escaping in addition to PowerShell's; say you want to pass the string to a C program, which expects embedded double-quotes to be escaped as \":

foo.exe "3\`" of rain"

Note how both `" - to make PowerShell happy - and the \ - to make the target program happy - must be present.

The same logic applies to invoking a batch file, where "" must be used:

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

By contrast, embedding single-quotes in a double-quoted string requires no escaping at all.

Single-quotes inside single-quoted strings do not require extra escaping; consider '2'' of snow', which is PowerShell' representation of 2' of snow.

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

PowerShell translates single-quoted strings to double-quoted ones before passing them to the target program.

However, double-quotes inside single-quoted strings, which do not need escaping for PowerShell, do still need to be escaped for the target program:

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

PowerShell v3 introduced the magic --% option, called the stop-parsing symbol, which alleviates some of the pain, by passing anything after it uninterpreted to the target program, save for cmd.exe-style environment-variable references (e.g., %USERNAME%), which are expanded; e.g.:

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

Note how escaping the embedded " as \" for the target program only (and not also for PowerShell as \`") is sufficient.

However, this approach:

  • does not allow for escaping % characters in order to avoid environment-variable expansions.
  • precludes direct use of PowerShell variables and expressions; instead, the command line must be built in a string variable in a first step, and then invoked with Invoke-Expression in a second.

An alternative workaround* that addresses this problem is to call via cmd /c with a single argument containing the entire command line:

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

Thus, despite its many advancements, PowerShell has not made escaping easier when calling external programs - on the contrary. It has, however, introduced support for single-quoted strings.

If you don't mind installing a third-party module (authored by me), the Native module (Install-Module Native) offers backward- and forward-compatible helper function ie, which obviates the need for the extra escaping and contains important accommodations for high-profile CLIs on Windows:

# Simply prepend 'ie' to your external-program calls.
ie foo.exe '3" of rain' -u $env:USERNAME
孤云独去闲 2024-07-20 15:57:24

谷歌最终给出了答案。 批量字符串替换的语法如下:

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

生成“replicate me”。 我的脚本现在如下所示:

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

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

Google eventually came up with the answer. The syntax for string replacement in batch is this:

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

Which produces "replicate me". My script now looks like this:

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

Which replaces all instances of " with \", properly escaped for bash.

羅雙樹 2024-07-20 15:57:24

作为 mklement0 的出色答案的补充:

几乎所有可执行文件都接受 \" 作为转义的 。 然而,在 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:"=\"!" 是安全版本。< /sup>


如果由于某种原因暂时启用 DELAYEDEXPANSION 不是一种选择,请继续阅读:

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

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

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

来源:每个人都以错误的方式引用命令行参数,请参阅“一种更好的引用方法”


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

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

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

As an addition to mklement0's excellent answer:

Almost all executables accept \" as an escaped ". Safe usage in cmd however is almost only possible using DELAYEDEXPANSION.
To explicitely send a literal " to some process, assign \" to an environment variable, and then use that variable, whenever you need to pass a quote. Example:

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

Note SETLOCAL ENABLEDELAYEDEXPANSION seems to work only within batch files. To get DELAYEDEXPANSION in an interactive session, start cmd /V:ON.

If your batchfile does't work with DELAYEDEXPANSION, you can enable it temporarily:

::region without DELAYEDEXPANSION

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

::region without DELAYEDEXPANSION

If you want to pass dynamic content from a variable that contains quotes that are escaped as "" you can replace "" with \" on expansion:

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

This replacement is not safe with %...% style expansion!

In case of OP bash -c "g++-linux-4.1 !v_params:"=\"!" is the safe version.


If for some reason even temporarily enabling DELAYEDEXPANSION is not an option, read on:

Using \" from within cmd is a little bit safer if one always needs to escape special characters, instead of just sometimes. (It's less likely to forget a caret, if it's consistent...)

To achieve this, one precedes any quote with a caret (^"), quotes that should reach the child process as literals must additionally be escaped with a backlash (\^"). ALL shell meta characters must be escaped with ^ as well, e.g. & => ^&; | => ^|; > => ^>; etc.

Example:

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

Source: Everyone quotes command line arguments the wrong way, see "A better method of quoting"


To pass dynamic content, one needs to ensure the following:
The part of the command that contains the variable must be considered "quoted" by cmd.exe (This is impossible if the variable can contain quotes - don't write %var:""=\"%). To achieve this, the last " before the variable and the first " after the variable are not ^-escaped. cmd-metacharacters between those two " must not be escaped. Example:

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

This isn't safe, if %dynamic_content% can contain unmatched quotes.

相思碎 2024-07-20 15:57:24

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

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

If the string is already within quotes then use another quote to nullify its action.

echo "Insert tablename(col1) Values('""val1""')" 
空城缀染半城烟沙 2024-07-20 15:57:24

在 Windows 10 21H1。

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

"C:\Program Files \Everything\Everything.exe" -search "<"""D:\我的间隔文件夹""" | """Z:\My_non_spaced_folder"""> <*.jpg | *.jpeg | *.avi | *.mp4>"

希望有帮助。

At Windows 10 21H1.

If from a batch (.bat) file I want to run the Everything application, I use """ inside double quotes argument:

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

Hope it helps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文