Joh*_*ith 7 powershell cmd escaping
我有一个Windows应用程序,在事件上,它调用这样的命令:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass "G:\test.ps1 -name '%x' -data '%y'"
Run Code Online (Sandbox Code Playgroud)
name参数有时会包含'在其中.有可能以某种方式逃脱吗?
Adi*_*bar 10
这实际上比你想象的要复杂得多.转义从cmd传递到PowerShell的字符串中的嵌套引号是一个令人头痛的问题.是什么让这个特别棘手的是你需要在传递给PowerShell脚本参数的单引号参数中传递给powershell.exe的引用参数中,用cmd扩展的变量进行替换.AFAIK cmd甚至没有基本字符串替换的任何本机功能,因此您需要PowerShell为您进行替换.
如果-data参数的参数(cmd变量x中包含的参数)不一定需要单引号,那么最简单的做法就是双引号,以便在x的值内单引号根本不需要逃脱.我说"最简单",但即使这样也有点棘手.正如Vasili Syrakis指出的那样,^通常是cmd中的转义字符,但要在(双引号)字符串中转义双引号,则需要使用a \.因此,您可以像这样编写批处理命令:
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'
Run Code Online (Sandbox Code Playgroud)
但是,如果X也可以包含在PowerShell中的特殊字符的字符插入串(",$,或`),然后就变成了LOT更加复杂.问题是%x是一个cmd变量,在PowerShell有机会触摸它之前,它会被cmd扩展.如果您在传递给powershell.exe的命令中单引号并且它包含单引号,那么您将为PowerShell会话提供一个早期终止的字符串,因此PowerShell没有机会执行任何操作对它的操作.以下显然不起作用,因为在替换任何内容之前,需要为-replace运算符提供有效的字符串:
'foo'bar' -replace "'", "''"
Run Code Online (Sandbox Code Playgroud)
另一方面,如果你对它进行双引号,那么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!"
Run Code Online (Sandbox Code Playgroud)
这假定在批处理文件中先前设置了x和y.如果您的应用只能向cmd发送单行命令,那么您需要将上述内容放入批处理文件中,并在开头添加以下两行:
set x=%~1
set y=%~2
Run Code Online (Sandbox Code Playgroud)
然后像这样调用批处理文件:
path\test.bat "%x%" "%y%"
Run Code Online (Sandbox Code Playgroud)
该~带出周边的命令行参数的报价.您需要引号才能在变量中包含空格,但引号也会添加到变量值中.批处理就是那样愚蠢.
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!
Run Code Online (Sandbox Code Playgroud)
因此,在命令中(为了清楚起见,从Duncan的答案修改为与OP的示例对齐):
powershell G:\test.ps1 -name '%x:'=''%' -data '%y:'=''%'
Run Code Online (Sandbox Code Playgroud)
'变量x和y中出现的所有内容都替换为'',并且命令扩展为
powershell G:\test.ps1 -name 'a''b' -data 'c''d'
Run Code Online (Sandbox Code Playgroud)
让我们分解一下这个关键因素'%x:'=''%':
'开头和结尾的两个s是显式外部引号传递给PowerShell引用参数,即OP所具有的相同单引号%x :'=''是字符串替换,表示'应替换为'' %x:'=''%扩展到该变量的值X与'被替换'',这是a''b 'a''b'此解决方案比上面的解决方法更简单地转义变量值中的单引号.但是,OP在更新中指出变量也可能包含双引号,到目前为止,此解决方案仍未将x中的双引号传递给PowerShell - 在PowerShell收到命令之前,这些仍会被cmd剥离.
好消息是,使用cmd字符串替换方法,这变得可以克服.在设置了x的初始值后执行以下cmd命令:
替换'为'',以逃避PowerShell的单引号:
set x=%x:'=''%
Run Code Online (Sandbox Code Playgroud)替换"为\",以逃避cmd的双引号:
set x=%x:"=\"%
Run Code Online (Sandbox Code Playgroud)
这两个任务的顺序无关紧要.
现在,可以使用OP首先使用的语法来调用PowerShell脚本(去掉powershell.exe的路径以使其全部适合一行):
powershell.exe -ExecutionPolicy Bypass "G:\test.ps1 -name '%x' -data '%y'"
Run Code Online (Sandbox Code Playgroud)同样,如果应用程序只能向cmd发送一行命令,这三个命令可以放在一个批处理文件中,应用程序可以调用批处理文件并传递上面显示的变量(原始答案中的第一个子弹) .
需要注意的一点是,如果替换"with \"是内联而不是使用单独的set命令,则不要"在字符串替换中转义s,即使它们位于双引号字符串中,即像这样:
powershell.exe -ExecutionPolicy Bypass "G:\test.ps1 -name '%x:"=\"' -data '%y'"
Run Code Online (Sandbox Code Playgroud)
...... 不是这样的:
powershell.exe -ExecutionPolicy Bypass "G:\test.ps1 -name '%x:\"=\\"' -data '%y'"
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
8003 次 |
| 最近记录: |