简体   繁体   English

命令行转义 PowerShell 的单引号

[英]Command line escaping single quote for PowerShell

I have a Windows application and on events, it calls a command like this:我有一个 Windows 应用程序和事件,它调用这样的命令:

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

The name parameter sometimes has ' in it. name 参数有时包含' Is it possible to escape that somehow?有可能以某种方式逃脱吗?

This is actually a lot trickier than you'd think.这实际上比你想象的要棘手得多。 Escaping nested quotes in strings passed from cmd to PowerShell is a major headache.转义从 cmd 传递到 PowerShell 的字符串中的嵌套引号是一个主要的问题。 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.使这个特别棘手的是,您需要在传递给 powershell.exe 的带引号的参数中由 cmd 扩展的变量中进行替换,该参数在传递给 PowerShell 脚本参数的单引号参数中。 AFAIK cmd doesn't have any native functionality for even basic string replacements, so you need PowerShell to do the replacement for you. AFAIK cmd 没有任何用于基本字符串替换的本机功能,因此您需要 PowerShell 为您进行替换。

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.如果-data参数(包含在 cmd 变量x中的那个)的参数不一定需要单引号,最简单的方法是双引号它,以便x值内的单引号根本不需要逃避。 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 \\ .正如 Vasili Syrakis 指出的, ^通常是 cmd 中的转义字符,但要在(双)引号字符串中转义双引号,您需要使用\\ So, you can write your batch command like this:因此,您可以像这样编写批处理命令:

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

That passes the following command to PowerShell:这会将以下命令传递给 PowerShell:

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

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:但是,如果X也包含特殊字符在PowerShell中插入串字符( "$` )的话,那就要复杂得多,但问题是,%×该得到由CMD前展开时,一个cmd变量PowerShell 有机会触及它。如果您在传递给 powershell.exe 的命令中单引号它并且它包含单引号,那么您将给 PowerShell 会话一个提前终止的字符串,因此 PowerShell 不会'没有机会对其执行任何操作。以下显然不起作用,因为在替换任何内容之前,需要为-replace运算符提供有效的字符串:

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

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.另一方面,如果您用双引号引用它,那么 PowerShell 会在对字符串执行任何替换之前插入该字符串,因此如果它包含任何特殊字符,它们会在可以通过替换进行转义之前被解释。 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.我在高低搜索了其他引用内联文字字符串的方法(相当于 perl 的q// ,其中除了您选择的分隔符外不需要转义任何内容),但似乎没有任何内容。

So, the only solution left is to use a here string, which requires a multi-line argument.因此,剩下的唯一解决方案是使用 here 字符串,这需要多行参数。 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!"
  • This assumes that x and y were set earlier in the batch file.这假设xy是在批处理文件中较早设置的。 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:如果您的应用程序只能向 cmd 发送单行命令,那么您需要将上述内容放入批处理文件中,在开头添加以下两行:

     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.批处理是愚蠢的。

  • The two blank lines following set LF=^ are required. set LF=^后面的两个空行是必需的。

That takes care of single quotes which also interpreting all other characters in the value of x literally, with one exception: double quotes.这会处理单引号,它也按字面解释x值中的所有其他字符,但有一个例外:双引号。 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.原因是,如上所述,批处理没有执行字符串替换的本机方式,并且x的值在 PowerShell 看到它之前由 cmd 扩展。

BTW... GOOD QUESTION!!顺便说一句......好问题!!


UPDATE:更新:

In fact, it turns out that it is possible to perform static string replacements in cmd.实际上,事实证明,它可以在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.这有点令人困惑,所以我将详细说明 Duncan 的解决方案中发生了什么。

The idea is that %var:hot=cold% expands to the value of the variable var , with all occurrences of hot replaced with cold :这个想法是%var:hot=cold%扩展到变量var的值,所有出现的hot替换为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!

So, in the command (modified from Duncan's answer to align with the OP's example, for the sake of clarity):因此,在命令中(为了清楚起见,从邓肯的回答中修改以与 OP 的示例保持一致):

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

all occurrences of ' in the variables x and y are replaced with '' , and the command expands to变量xy中出现的所有'都替换为'' ,命令扩展为

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

Let's break down the key element of that, '%x:'=''%' :让我们分解其中的关键元素'%x:'=''%'

  • The two ' s at the beginning and the end are the explicit outer quotes being passed to PowerShell to quote the argument, ie the same single quotes that the OP had around %x开头和结尾的两个' s 是传递给 PowerShell 以引用参数的显式外部引号,即 OP 在%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 %x:'=''%扩展为变量x的值,其中'替换为'' ,即a''b
  • Therefore, the whole thing expands to 'a''b'因此,整个事情扩展为'a''b'

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.但是,OP 在更新中指出该变量也可能包含双引号,到目前为止,该解决方案仍然没有将x 中的双引号传递给 PowerShell——在 PowerShell 收到命令之前,它们仍然会被 cmd 删除。

The good news is that with the cmd string replacement method, this becomes surmountable.好消息是,使用 cmd 字符串替换方法,这变得可以克服。 Execute the following cmd commands after the initial value of x has already been set:在设置x的初始值后执行以下 cmd 命令:

  1. Replace ' with '' , to escape the single quotes for PowerShell:'替换为'' ,以便为 PowerShell 转义单引号:

     set x=%x:'=''%
  2. Replace " with \\" , to escape the double quotes for cmd:"替换为\\" ,以转义 cmd 的双引号:

     set x=%x:"=\\"%

    The order of these two assignments doesn't matter.这两个任务的顺序无关紧要。

  3. 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):现在,可以使用 OP 最初使用的语法调用 PowerShell 脚本(删除了 powershell.exe 的路径以将其全部放在一行中):

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

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).同样,如果应用程序只能向 cmd 发送一行命令,则可以将这三个命令放在一个批处理文件中,应用程序可以调用批处理文件并传递如上所示的变量(我原来的答案中的第一个项目符号) .

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, ie like this:一个有趣的一点需要注意的是,如果更换"\\"是行内完成,而不是用一独立命令,你不要逃避"的字符串替换S,即使他们是一个双引号字符串中,即像这样:

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

I'm slightly unclear in the question whether %x and %y are CMD variables (in which case you should be using %x% to substitute it in, or a substitution happening in your other application.我有点不清楚%x%y是否是 CMD 变量(在这种情况下,您应该使用%x%来替换它,或者在您的其他应用程序中进行替换。

You need to escape the single quote you are passing to PowerShell by doubling it in the CMD.EXE command line.您需要通过在 CMD.EXE 命令行中加倍来转义传递给 PowerShell 的单引号。 You can do this by replacing any quotes in the variable with two single quotes.您可以通过用两个单引号替换变量中的任何引号来做到这一点。

For example:例如:

C:\scripts>set X=a'b

C:\scripts>set Y=c'd

C:\scripts>powershell .\test.ps1 -name '%x:'=''%' '%y:'=''%'
Name is 'a'b'
Data is 'c'd'

where test.ps1 contains:其中test.ps1包含:

C:\scripts>type test.ps1
param($name,$data)

write-output "Name is '$name'"
write-output "Data is '$data'"

If the command line you gave is being generated in an external application, you should still be able to do this by assigning the string to a variable first and using & to separate the commands (be careful to avoid trailing spaces on the set command).如果您提供的命令行是在外部应用程序中生成的,您仍然可以通过先将字符串分配给变量并使用&分隔命令来执行此操作(注意避免set命令上的尾随空格)。

set X=a'b& powershell .\test.ps1 -name '%x:'=''%'

The CMD shell supports both a simple form of substitution, and a way to extract substrings when substituting variables. CMD shell 既支持简单的替换形式,也支持在替换变量时提取子字符串的方法。 These only work when substituting in a variable, so if you want to do multiple substitutions at the same time, or substitute and substring extraction then you need to do one at a time setting variables with each step.这些仅在替换变量时有效,因此如果您想同时进行多个替换,或者替换和子字符串提取,那么您需要在每个步骤中一次设置一个变量。

Environment variable substitution has been enhanced as follows:

    %PATH:str1=str2%

would expand the PATH environment variable, substituting each occurrence
of "str1" in the expanded result with "str2".  "str2" can be the empty
string to effectively delete all occurrences of "str1" from the expanded
output.  "str1" can begin with an asterisk, in which case it will match
everything from the beginning of the expanded output to the first
occurrence of the remaining portion of str1.

May also specify substrings for an expansion.

    %PATH:~10,5%

would expand the PATH environment variable, and then use only the 5
characters that begin at the 11th (offset 10) character of the expanded
result.  If the length is not specified, then it defaults to the
remainder of the variable value.  If either number (offset or length) is
negative, then the number used is the length of the environment variable
value added to the offset or length specified.

    %PATH:~-10%

would extract the last 10 characters of the PATH variable.

    %PATH:~0,-2%

would extract all but the last 2 characters of the PATH variable.

我相信你可以用^逃脱它:

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

Try encapsulating your random single quote variable inside a pair of double quotes to avoid the issue.尝试将您的随机单引号变量封装在一对双引号内以避免该问题。

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

The problem arises because you used single quotes and the random extra single quote appearing inside the single quotes fools PowerShell.出现问题是因为您使用了单引号,并且单引号内出现的随机额外单引号愚弄了 PowerShell。 This shouldn't occur if you double quote with backticks, as the single quote will not throw anything off inside double quotes and the backticks will allow you to double/double quote.如果您使用反引号双引号,则不会发生这种情况,因为单引号不会在双引号内抛出任何内容,反引号将允许您双引号/双引号。

Just FYI, I ran into some trouble with a robocopy command in powershell and wanting to exclude a folder with a single quote in the name (and backquote didn't help and ps and robocopy don't work with double quote);仅供参考,我在 powershell 中使用 robocopy 命令遇到了一些麻烦,并希望排除名称中带有单引号的文件夹(反引号没有帮助,ps 和 robocopy 不能使用双引号); so, I solved it by just making a variable for the folder with a quote in it:所以,我通过为文件夹创建一个带有引号的变量来解决它:

$folder="John Doe's stuff"
robocopy c:\users\jd\desktop \\server\folder /mir /xd 'normal folder' $folder

One wacky way around this problem is to use echo in cmd to pipe your command to 'powershell -c "-" ' instead of using powershell "arguments"解决此问题的一种古怪方法是在 cmd 中使用 echo 将您的命令通过管道传递给 'powershell -c "-" ' 而不是使用 powershell "arguments"

for instance:例如:

ECHO Write-Host "stuff'" | powershell -c "-"

output:输出:

stuff'

Couple of notes:几个注意事项:

  1. Do not quote the command text you are echoing or it won't work不要引用您正在回显的命令文本,否则它将不起作用

  2. If you want to use any pipes in the PowerShell command they must be triple-escaped with carats to work properly如果你想在 PowerShell 命令中使用任何管道,它们必须用克拉进行三次转义才能正常工作

    ^^^| ^^^|

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM