命令行转义PowerShell的单引号

问题描述 投票:6回答:4

我有一个Windows应用程序,在事件上,它调用这样的命令:

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

name参数有时会包含'。有可能以某种方式逃脱吗?

powershell cmd escaping
4个回答
9
投票

这实际上比你想象的要复杂得多。转义从cmd传递到PowerShell的字符串中的嵌套引号是一个令人头痛的问题。是什么让这个特别棘手的是你需要在传递给PowerShell脚本参数的单引号参数中传递给powershell.exe的引用参数中,用cmd扩展的变量进行替换。即使是基本的字符串替换,AFAIK cmd也没有任何本机功能,因此您需要PowerShell为您进行替换。

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

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

这将以下命令传递给PowerShell:

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

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

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

另一方面,如果你对它进行双引号,那么PowerShell会在对字符串执行任何替换之前对其进行插值,因此如果它包含任何特殊字符,则在对它们进行替换之前对它们进行转义。我搜索高低不同的其他方式来引用内联的文字字符串(相当于perl的q //,其中没有任何东西需要被转义但是你选择的分隔符),但似乎没有任何东西。

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

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发送单行命令,那么您需要将上述内容放入批处理文件中,并在开头添加以下两行: set x=%~1 set y=%~2 然后像这样调用批处理文件: path\test.bat "%x%" "%y%" ~删除了命令行参数周围的引号。您需要引号才能在变量中包含空格,但引号也会添加到变量值中。批处理就是那样愚蠢。
  • 需要set LF=^之后的两个空白行。

这样可以处理单引号,它也可以解释x值的所有其他字符,但有一个例外:双引号。不幸的是,如果双引号可能是您在评论中指出的值的一部分,我不认为如果不使用第三方实用程序就可以克服该问题。原因是,如上所述,批处理没有执行字符串替换的本机方式,并且在PowerShell看到之前x的值被cmd扩展。

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


更新:

实际上,事实证明可以在cmd中执行静态字符串替换。 Duncan补充说明了如何做到这一点。这有点令人困惑,所以我将详细说明Duncan的解决方案中发生了什么。

这个想法是%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!

因此,在命令中(为了清楚起见,从Duncan的答案修改为与OP的示例对齐):

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

变量x和y中出现的所有'都被''替换,并且命令扩展为

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

让我们分解一下'%x:'=''%'的关键要素:

  • 开头和结尾的两个's是明确的外部引号传递给PowerShell来引用该参数,即OP在%x周围的相同单引号
  • :'=''是字符串替换,表明'应该替换为''
  • %x:'=''%扩展到变量x的值,'''取代,这是a''b
  • 因此,整个事情扩展到'a''b'

此解决方案比上面的解决方法更简单地转义变量值中的单引号。但是,OP在更新中指出变量也可能包含双引号,到目前为止,此解决方案仍然没有将x中的双引号传递给PowerShell - 在PowerShell收到命令之前,这些仍然被cmd剥离。

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

  1. '替换'',以逃避PowerShell的单引号: set x=%x:'=''%
  2. "替换\",以逃避cmd的双引号: set x=%x:"=\"% 这两个任务的顺序无关紧要。
  3. 现在,可以使用OP首先使用的语法来调用PowerShell脚本(去掉powershell.exe的路径以使其全部适合一行): powershell.exe -ExecutionPolicy Bypass "G:\test.ps1 -name '%x' -data '%y'"

同样,如果应用程序只能向cmd发送一行命令,这三个命令可以放在一个批处理文件中,应用程序可以调用批处理文件并传递上面显示的变量(原始答案中的第一个子弹) 。

需要注意的一点是,如果使用"替换\"是内联而不是使用单独的set命令,则不要在字符串替换中转义"s,即使它们位于双引号字符串中,即像这样:

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

......不是这样的:

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

3
投票

我在%x%y是CMD变量这个问题上有点不清楚(在这种情况下你应该使用%x%替换它,或者在你的其他应用程序中发生替换)。

您需要通过在CMD.EXE命令行中加倍来转义要传递给PowerShell的单引号。您可以通过用两个单引号替换变量中的任何引号来完成此操作。

例如:

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'

其中test.ps1包含:

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

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

如果您在外部应用程序中生成了您提供的命令行,您仍然可以通过首先将字符串分配给变量并使用&来分隔命令(小心避免set命令上的尾随空格)来完成此操作。

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

CMD shell支持简单的替换形式,以及在替换变量时提取子串的方法。这些仅在替换变量时起作用,因此如果您想同时进行多次替换,或者替换和子串提取,那么您需要一次一个地设置每个步骤的变量。

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.

1
投票

我相信你可以用qazxsw poi逃脱它:

^


0
投票

尝试将随机单引号变量封装在一对双引号内,以避免出现问题。

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

问题出现是因为你使用单引号和单引号内出现的随机额外单引号傻瓜PowerShell。如果你用反引号加双引号就不会发生这种情况,因为单引号不会在双引号内抛出任何东西,并且反引号会允许你加倍/双引号。

© www.soinside.com 2019 - 2024. All rights reserved.