本文介绍了PowerShell的命令行转义单引号的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个Windows应用程序,发生事件时,它会调用如下命令:

I have a Windows application and on events, it calls a command like this:

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass "G:\test.ps1 -name '%x' -data '%y'"

name参数有时包含'。

The name parameter sometimes has ' in it. Is it possible to escape that somehow?

推荐答案

这实际上比您想象的要棘手得多。在从cmd传递到PowerShell的字符串中转义嵌套的引号是一个主要的麻烦。使得这一问题特别棘手的是,您需要在传递给PowerShell脚本参数的单引号内,将传递给powershell.exe的带引号的参数中由cmd扩展的变量替换。 AFAIK cmd甚至没有用于基本字符串替换的任何本机功能,因此您需要PowerShell来为您进行替换。

This is actually a lot trickier than you'd think. Escaping nested quotes in strings passed from cmd to PowerShell is a major headache. What makes this one especially tricky is that you need to make the replacement in a variable expanded by cmd in the quoted argument passed to powershell.exe within a single-quoted argument passed to a PowerShell script parameter. AFAIK cmd doesn't have any native functionality for even basic string replacements, so you need PowerShell to do the replacement for you.

如果-数据参数(cmd变量 x 中包含的参数)不一定需要用单引号引起来,最简单的方法是将其双引号,以便将单引号引起来。在 x 的值范围内,根本不需要转义。我说最简单,但即使这样也有些棘手。正如Vasili Syrakis指出的那样, ^ 通常是cmd中的转义字符,但是要在(双引号)字符串中转义双引号,您需要使用 \ 。因此,您可以这样编写批处理命令:

If the argument to the -data paramater (the one contained in the cmd variable x) doesn't necessarily need to be single-quoted, the simplest thing to do is to double-quote it, so that single quotes within the value of x don't need to be escaped at all. I say "simplest", but even that is a little tricky. As Vasili Syrakis indicated, ^ is normally the escape character in cmd, but to escape double quotes within a (double-)quoted string, you need to use a \. So, you can write your batch command like this:

C:\Windows\System32\WindowsPowerShell\v1.0\ powershell.exe -ExecutionPolicy绕过 G:\test.ps1 -name \%x%\ -data'%y%'

将以下命令传递给PowerShell:

That passes the following command to PowerShell:

G:\test.ps1 -name "value of x, which may contain 's" -data 'value of y'

但是,如果 x 也可以包含PowerShell插值字符串中的特殊字符( , $ 或`),那么它变得更复杂了 LOT 。问题在于%x 是一个cmd变量,在PowerShell有机会接触它之前被cmd扩展。如果在命令中将其单引号传递给powershell.exe,并且它包含单引号,则表示正在执行PowerShell会话提早终止的字符串,因此PowerShell没有机会nity对其执行任何操作。以下内容显然不起作用,因为在替换任何内容之前,需要为 -replace 运算符提供有效的字符串:

If, however, x can also contain characters that are special characters in PowerShell interpolated strings (", $, or `), then it becomes a LOT more complicated. The problem is that %x is a cmd variable that gets expanded by cmd before PowerShell has a chance to touch it. If you single-quote it in the command you're passing to powershell.exe and it contains a single quote, then you're giving the PowerShell session a string that gets terminated early, so PowerShell doesn't have the opportunity to perform any operations on it. The following obviously doesn't work, because the -replace operator needs to be supplied a valid string before you can replace anything:

'foo'bar' -replace "'", "''"

另一方面,如果将双引号括起来,那么PowerShell会在对该字符串执行任何替换之前先对字符串进行插值,因此,如果包含任何特殊字符,则在对它们进行替换之前,先对它们进行解释。我在上下搜索了其他以内联方式引用文字字符串的方式(与perl的 q // 等效,在其中无需逃脱,但您选择的分隔符),但是似乎没有

On the other hand, if you double-quote it, then PowerShell interpolates the string before it performs any replacements on it, so if it contains any special characters, they're interpreted before they can be escaped by a replacement. I searched high and low for other ways to quote literal strings inline (something equivalent to perl's q//, in which nothing needs to be escaped but the delimiter of your choice), but there doesn't seem to be anything.

因此,剩下的唯一解决方案是使用here字符串,该字符串需要多行参数。在批处理文件中这很棘手,但是可以做到:

So, the only solution left is to use a here string, which requires a multi-line argument. That's tricky in a batch file, but it can be done:

setlocal EnableDelayedExpansion
set LF=^


set pscommand=G:\test.ps1 -name @'!LF!!x!!LF!'@ -data '!y!'
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass "!pscommand!"




  • 这假设 x y 在批处理文件中设置得较早。如果您的应用只能向cmd发送单行命令,则需要将以上内容放入批处理文件中,并在开头添加以下两行:

    • This assumes that x and y were set earlier in the batch file. If your app can only send a single-line command to cmd, then you'll need to put the above into a batch file, adding the following two lines to the beginning:

      set x=%~1
      set y=%~2
      

      然后像这样调用批处理文件:

      Then invoke the batch file like this:

      path\test.bat "%x%" "%y%"
      

      〜会消失命令行参数周围的引号。您需要引号以在变量中包含空格,但引号也会添加到变量值中。

      The ~ strips out the quotes surrounding the command line arguments. You need the quotes in order to include spaces in the variables, but the quotes are also added to the variable value. Batch is stupid that way.

      在 set LF = ^ 之后的两个空行是必需的。 / p>


    • The two blank lines following set LF=^ are required.

      这将处理单引号,该单引号还会解释 x 值中的所有其他字符。从字面上看,除了一个例外:双引号。不幸的是,如果您在注释中指出双引号可能是值的一部分,那么我认为如果不使用第三方实用程序,这个问题是无法解决的。原因是,如上所述,batch没有执行字符串替换的本机方式,并且在PowerShell看到之前,cmd扩展了 x 的值。

      That takes care of single quotes which also interpreting all other characters in the value of x literally, with one exception: double quotes. Unfortunately, if double quotes may be part of the value as you indicated in a comment, I don't believe that problem is surmountable without the use of a third party utility. The reason is that, as mentioned above, batch doesn't have a native way of performing string replacements, and the value of x is expanded by cmd before PowerShell ever sees it.

      顺便说一下... 好问题!!

      更新:

      实际上,事实证明 可以在cmd中执行静态字符串替换。邓肯(Duncan)添加了一个答案,展示了如何做到这一点。这有点令人困惑,所以我将详细说明Duncan解决方案中的情况。

      In fact, it turns out that it is possible to perform static string replacements in cmd. Duncan added an answer that shows how to do that. It's a little confusing, so I'll elaborate on what's going on in Duncan's solution.

      想法是%var:hot = cold% 扩展为变量 var 的值,所有出现的 hot 都替换为 cold :

      The idea is that %var:hot=cold% expands to the value of the variable var, with all occurrences of hot replaced with cold:

      D:\Scratch\soscratch>set var=You're such a hot shot!
      
      D:\Scratch\soscratch>echo %var%
      You're such a hot shot!
      
      D:\Scratch\soscratch>echo %var:hot=cold%
      You're such a cold scold!
      

      因此,在命令中(为了方便起见,从Duncan的答案修改为与OP的示例保持一致)清晰度):

      So, in the command (modified from Duncan's answer to align with the OP's example, for the sake of clarity):

      powershell G:\test.ps1 -name '%x:'=''%' -data '%y:'=''%'
      

      所有出现的' x 和 y 中的code>替换为'',命令扩展为

      all occurrences of ' in the variables x and y are replaced with '', and the command expands to

      powershell G:\test.ps1 -name 'a''b' -data 'c''d'
      

      让我们分解其中的关键元素,'%x:'=''% ':

      Let's break down the key element of that, '%x:'=''%':


      • 两个'开头和结尾是传递给PowerShell的显式外部引号,以引用该参数,即,OP在%x
      • $ b $附近使用相同的单引号b
      • :'=''是字符串替换,指示'应该替换为''

      • %x:'=''%扩展到变量 x 的值,其中'替换为'',即 a''b

      • 因此,整个内容扩展为'a''b'

      • The two 's at the beginning and the end are the explicit outer quotes being passed to PowerShell to quote the argument, i.e. the same single quotes that the OP had around %x
      • :'='' is the string replacement, indicating that ' should be replaced with ''
      • %x:'=''% expands to the value of the variable x with ' replaced by '', which is a''b
      • Therefore, the whole thing expands to 'a''b'

      此解决方案比上面的解决方法更简单地转义了变量值中的单引号。但是,OP在更新中指示该变量可能还包含双引号,并且到目前为止,该解决方案仍未在 x 中将双引号传递给PowerShell-那些仍被cmd删除PowerShell会接收命令。

      This solution escapes the single quotes in the variable value much more simply than my workaround above. However, the OP indicated in an update that the variable may also contain double quotes, and so far this solution still doesn't pass double quotes within x to PowerShell--those still get stripped out by cmd before PowerShell receives the command.

      好消息是,使用cmd字符串替换方法,这变得可以克服。设置 x 的初始值后,请执行以下cmd命令:

      The good news is that with the cmd string replacement method, this becomes surmountable. Execute the following cmd commands after the initial value of x has already been set:


      1. Replace '与'',以避开PowerShell的单引号:

      1. Replace ' with '', to escape the single quotes for PowerShell:

      set x=%x:'=''%
      


    • 替换为 \ ,以转义cmd的双引号:

    • Replace " with \", to escape the double quotes for cmd:

      set x=%x:"=\"%
      

      这两个任务的顺序无关紧要。

      The order of these two assignments doesn't matter.

      现在,可以使用OP最初使用的语法来调用PowerShell脚本(删除了powershell.exe的路径以使其完全适合行):

      Now, the PowerShell script can be called using the syntax the OP was using in the first place (path to powershell.exe removed to fit it all on one line):

      powershell.exe -ExecutionPolicy Bypass "G:\test.ps1 -name '%x' -data '%y'"
      


    • 同样,如果应用只能将单行命令发送到cmd,这三个命令可以放置在批处理文件中,应用程序可以调用该批处理文件并传递变量,如上所示(我的原始答案中的第一个项目符号)。

      Again, if the app can only send a one-line command to cmd, these three commands can be placed in a batch file, and the app can call the batch file and pass the variables as shown above (first bullet in my original answer).

      需要注意的有趣一点是,如果将 替换为 \ 是内联完成的,而不是使用单独的 set 命令执行的,因此您不要在其中的 进行转义字符串替换,即使它们位于双引号字符串内,例如:

      One interesting point to note is that if the replacement of " with \" is done inline rather than with a separate set command, you don't escape the "s in the string replacement, even though they're inside a double-quoted string, i.e. like this:

      powershell.exe -ExecutionPolicy Bypass "G:\test.ps1 -name '%x:"=\"' -data '%y'"
      

      ... 不是 ,像这样:

      ...not like this:

      powershell.exe -ExecutionPolicy Bypass "G:\test.ps1 -name '%x:\"=\\"' -data '%y'"
      

      这篇关于PowerShell的命令行转义单引号的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-05 04:11