PowerShell从命令行参数中剥离双引号

32 浏览
0 Comments

PowerShell从命令行参数中剥离双引号

最近在使用PowerShell时,遇到了一些使用GnuWin32时出现的问题,特别是在涉及到双引号的时候。

进一步调查后发现,即使正确转义,PowerShell也会从命令行参数中删除双引号。

注意当传递给PowerShell的echo cmdlet时,双引号是存在的,但是当作为参数传递给echo.exe时,双引号会被删除,除非用反斜杠转义(尽管PowerShell的转义字符是反引号,而不是反斜杠)。

这对我来说似乎是一个bug。如果我向PowerShell传递了正确转义的字符串,那么PowerShell应该处理调用命令时可能需要的任何转义。

这是怎么回事?

目前,解决方法是根据以下规则转义命令行参数(PowerShell用于调用.exe文件的CreateProcess API调用(间接)使用的规则):

  • 要传递一个双引号,请用反斜杠转义:\" -> "
  • 要传递一个或多个反斜杠后跟一个双引号,请用另一个反斜杠转义每个反斜杠并转义双引号:\\\\\" -> \\"
  • 如果后面没有双引号,则不需要转义反斜杠:\\ -> \\

请注意,可能需要进一步转义双引号以转义Windows API转义字符串中的双引号到PowerShell。

这里有一些示例,使用来自GnuWin32的echo.exe:

PS C:\Documents and Settings\Nick> echo.exe "\`""

"

PS C:\Documents and Settings\Nick> echo.exe "\\\\\`""

\\"

PS C:\Documents and Settings\Nick> echo.exe "\\"

\\

我想如果你需要传递一个复杂的命令行参数,这可能会很快变得混乱。当然,这些都没有在CreateProcess()或PowerShell文档中记录。

还要注意的是,将带有双引号的参数传递给.NET函数或PowerShell cmdlet是不必要的。对于这个,你只需要将双引号转义为PowerShell即可。

编辑:正如Martin在他的优秀答案中指出的那样,这在CommandLineToArgv()函数(CRT用于解析命令行参数的函数)文档中有记录。

0
0 Comments

问题的原因是在PowerShell中传递参数给外部程序时,存在额外的引号评估层次。这导致参数在传递给外部命令时被重新解析,而不是按照原样传递。

解决方法是通过在参数值中嵌套引号来绕过额外的引号评估层次。可以将参数值设置为包含嵌套引号的数组,并在数组中添加足够的引号来覆盖额外的隐藏评估。

以下是解决方法的示例代码:

$aa = 'arg="foo"', 'arg=""""bar""""'

通过这种方式处理参数可以确保它们以原样传递给外部命令。这对于包含特殊字符的参数值非常有用,因为它们不会被重新解析和修改。

此外,这种方法还允许在字符串中展开PowerShell变量。例如,可以使用`"name=`"$value`""`来展开PowerShell变量并得到`name=""`。

通过使用这种方法,可以避免在PowerShell中传递参数时引号被剥离的问题。这样可以确保参数以原样传递给外部程序,并保持参数值的完整性。

0
0 Comments

PowerShell在处理命令行参数时,会剥离双引号,即使这些引号已经被正确转义。这使得一些命令行参数无法正确传递给调用的命令。这个问题的根源在于Windows进程接收到的单字符串命令行需要使用的CommandLineToArgvW函数。该函数的转义规则非常不直观,导致了这个问题的出现。

PowerShell在处理命令时会进行更多的解析,因为它需要解析变量和处理多个参数。在Powershell中,如果传递给本地命令的单个参数包含空格,并且这些空格与不成对的双引号混合使用,Powershell会自动将其加上引号。这使得某些字符串无法作为单个参数传递给子进程。

然而,在Powershell 5之前的版本中,Powershell对这些规则知之甚少,也没有提供正确的解决方法。但是在Powershell 7中,解决了这个问题,只要所有的引号都使用\"转义,并且经过CommandLineToArgvW处理,就可以将任意字符串从Powershell传递给使用CommandLineToArgvW的子进程。

为了解决这个问题,我们需要正确转义参数中的引号和反斜杠,并确定Powershell是否会自动引用转义后的字符串。如果Powershell不会自动引用转义后的字符串,我们需要手动引用它,并确保Powershell不会再次引用它。由于代码过长,完整的解决方案可以在Gist中查看

这个问题的解决方案并不完美,对于某些带有引号的字符串和Powershell 5,你必须在传递的参数中添加前导空格。对于使用gcc编译C源代码并尝试定义带引号的宏的情况,我们无法确定是否可以成功,建议进行尝试。

这个问题的出现是因为CommandLineToArgvW函数的转义规则不直观,Powershell在处理命令行参数时对这些规则了解有限。解决这个问题需要对参数进行正确的转义,并确定Powershell是否会自动引用转义后的字符串。

0
0 Comments

PowerShell在处理需要引号的字符串参数时存在问题,这导致了无法正确执行需要引号的命令。这个问题已经被反馈给了Microsoft,并且被认为是一个已知问题。这个问题使得PowerShell无法作为一个通用的命令行工具,因为无法简单地执行像执行sqlcmd这样的命令。在SQL Server 2008中,使用SqlCmd命令时,有一个-v参数接受一系列的name:value参数。如果value中包含空格,就必须对其进行引号引用。然而,由于没有一个确定的方法来正确编写调用这个应用程序的命令行,所以即使掌握了4或5种不同的引号和转义方式,仍然无法确定哪种方式在什么情况下起作用。另外,也可以通过调用cmd来解决这个问题。

这个问题在一年半之后仍然没有得到修复,尽管它被标记为“已修复”,但却没有提供修复的链接或使用方法。某些情况下该链接已经失效了。

还有一个关于文件名中包含空格的情况,当使用CMD /C命令时,需要在命令中的文件名两边添加引号。在Windows 7中需要在命令前后添加额外的引号,但在Windows 10中会报错。可以通过判断系统版本来解决这个问题。

有人认为这个问题无法解决,因为Windows的CreateProcess函数存在问题,可能需要引入CreateProcessExSquared API来解决。但有趣的是,Windows试图模仿Unix的一切都是文本,但却因为过多的无结构文本而遭到了困扰,每个目标可执行文件都必须从CreateProcess传递的单个字符串中解析出自己的引号。

总之,这个问题在PowerShell中存在,导致无法正确处理需要引号的命令行参数,可能需要通过调用cmd来解决。另外,也有人认为这个问题无法解决。

0