如何在PowerShell中转义动态变量的内容?

时间:2018-08-28 15:45:07

标签: powershell

比方说,我有一个变量$password = Get-Passwd ACME\bob,该变量用于获取给定用户的密码。密码包含PowerShell所使用的各种特殊字符,包括$

我需要在以下命令中动态使用密码:

cmdkey.exe /add:$hostname /user:"$user" /pass:"`"$password`""

我需要在两侧都转义"字符,以便将命令解析为pass:"123qwe"而不是pass:123qwe。问题是,当密码包含$字符时,密码将中断。如何在不破坏密码的情况下将密码传递给该命令?

2 个答案:

答案 0 :(得分:2)

在PowerShell中运行可执行文件

对于大多数可执行文件,无需在PowerShell中手动引用参数。只需使用任何变量编写命令,PowerShell解析器将自动引用程序命令行的参数。通常这“行之有效”。

cmdkey.exe /add:hostname /user:username /pass:$pass

您可以使用我编写的名为showargs.exe的小程序检查PowerShell传递给可执行文件的 actual 命令行,该程序可在以下页面中找到:

https://www.itprotoday.com/management-mobility/running-executables-powershell

示例:

showargs cmdkey.exe /add:hostname /user:username /pass:$pass

showargs.exe程序只是将其命令行回显到标准输出,因此您可以看到PowerShell 实际上传递给可执行文件的文字命令行。

嵌入$字符应该没问题。如果该字符在PowerShell字符串变量中,PowerShell会将其作为命令行字符串的一部分传递。示例:

$pass = 'my$pass'
cmdkey.exe /add:hostname /user:username /password:$pass

无论您如何将命令行传递给程序,请务必注意,该命令行的解释取决于单个程序的 。 (例如,如果可执行文件的解析器不支持某些字符,则无需使用大量引号或解析即可解决该限制。)

Cmdkey.exe使用非标准解析器

在对cmdkey.exe的功能测试中,似乎没有办法在其命令行上“转义” "字符。由于情况似乎如此,因此您将无法使用cmdkey.exe来存储包含嵌入式"字符的凭据。

在Cmdkey.exe命令行参数中嵌入空格

由于cmdkey.exe使用非标准的命令行解析器,因此您不能在其命令行上使用包含嵌入式空格的变量。例如:

PS C:\> $pass = "my pass"
PS C:\> showargs cmdkey.exe /add:hostname /user:username /password:$pass
cmdkey.exe /add:hostname /user:username "/password:my pass"

"/password:my pass"显然使cmdkey.exe解析器感到困惑,因此我们必须绕过PowerShell的默认解析行为来解决此问题。最简单的方法是通过在包含空格的参数周围转引号:

PS C:\> showargs.exe cmdkey.exe /add:hostname /user:username /password:`"$pass`"
cmdkey.exe /add:hostname /user:username /password:"my pass"

在任何情况下,您都可以使用showargs.exe来诊断问题并制定适合于您需要运行的可执行文件的解决方案。

答案 1 :(得分:1)

tl; dr

  • 您的命令应该有效-除非$password包含"个字符。

      相比之下,
    • 嵌入$个字符。不是是个问题。
  • 您的/pass:"`"$password`""技术-即显式的嵌入式双引号-也可以正确处理带有嵌入空格的值,这与建议的/pass:$password技术不同在Bill Stewart's helpful answer中。

    • 您可以通过省略外面的"..."引号来简化命令,如Bill的答案中所述:
      /pass:`"$password`"
  • 关于支持"字符。值:即使使用\进行转义也不一定总是有效,即在涉及空格的情况下-请参见下面的详细信息。


  

问题在于,当密码包含$字符时,密码会中断。

您的命令将按原样将嵌入$值中的$password个字符传递到目标程序。

因此,如果有问题,则意味着您的 target 程序-cmdkey.exe-解释了$个字符,但请注意,docs没有提及。
但是,如果确实如此,则必须按目标程序的要求转义$个字符 ,以便从字面上传递它们。

注意:有一个基本限制,但是:

如果参数值包含空格嵌入" 字符,则命令通常会中断,无论后者是否已正确转义为是否有目标程序。

通常,您会将值内部的"转换为\",以免破坏值的双引号:

# !! Only works if:
# !!  * $password contains NO "
# !!  * $password contains " but NOT ALSO SPACES
# !!  * $password contains PAIRS of " and what is between each
# !!    pair does not contain spaces
cmdkey.exe /add:$hostname /user:"$user" /pass:`"$($password -replace '"', '\"')`"

注意:
*将嵌入式"转换为\"本身并不是一个标准,但是大多数外部程序都认可它;唯一值得注意的例外是批处理文件-有关详细信息,请参见this answer
*可以说,PowerShell应该自动处理此转义 -有关详细信息,请参见this GitHub issue

如果Windows PowerShell认为它不能按原样将结果令牌作为单个参数传递给目标程序,则它会盲目地在 entire 令牌周围应用双引号< sup> [1] ,与转义的"结合使用,可能会导致语法无效:

例如,如果$password从字面上包含a " b,PowerShell将使用上面的命令最终在幕后传递以下内容:

 ...  "/pass:"a \" b""

也就是说,即使大多数目标程序,PowerShell看到的结果文字令牌-/pass:"a \" b"都被盲目地括在双引号中 ,整体上是 将按原样正确解析/pass:"a \" b"
结果,显式提供的双引号无效(因为这将需要另一级转义)-并且缺少使用--%(停止解析符号),然后将其限制为 literals < / em>(或%...%样式的环境变量引用),则无法解决。

If 如果目标程序将整个都用双引号括起来,则目标程序会识别该参数(例如,
"/pass:a b"而非/pass:"a b"),您可以省略参数 value 周围的显式双引号。
但是请注意,如果目标参数(包括cmdkey.exe)被双引号整体引用,则 not 不会识别。

... /pass:$($password -replace '"', '\"')

$password确实包含a " b的情况下,PowerShell随后通过了幕后:

... "/pass:a \" b"

这在语法上是有效的-对于将\"识别为转义的"(这是规范)的目标程序-但如上所述, entire 参数包含在“ ...”中,并且目标程序可能不仅支持 value


[1] Windows PowerShell忽略结果令牌中的任何\转义,并认为 all 嵌入的"具有语法功能:从这个角度来看,如果令牌不是由直接串联的未加引号和双引号的字符串组成的任何组合,盲目将双引号括起来,这可能会破坏命令。
这种行为不仅晦涩难懂,而且还会阻止可靠,可预测的参数传递。
PowerShell Core 现在将\"识别为已转义;但是,未预先转义为\"的引号仍然会导致引号损坏;例如,'a "b c" d'作为"a "b c" d"传递,目标程序将解析为 2 自变量a bc d(在引号删除之后)。