powershell string urldecoding 执行时间

powershell string urldecoding execution time

通过 powershell oneliner 在 CGI 混合脚本中执行 URL 解码:

echo %C4%9B%C5%A1%C4%8D%C5%99%C5%BE%C3%BD%C3%A1%C3%AD%C3%A9%C5%AF%C3%BA| powershell.exe "Add-Type -AssemblyName System.Web;[System.Web.HttpUtility]::UrlDecode($Input) | Write-Host"

这个 oneliner 在虚拟机上的执行时间在 2-3 秒之间。是因为使用了 .NET 对象吗?有什么办法可以减少执行时间吗?也有一些 C 写的,快如闪电 urldecode.exe 实用程序,但不幸的是它不吃 STDIN。

如果您将输入数据作为字符串 文字 传递,请注意,如问题中的示例调用(而不是 命令):

  • 如果您从 interactive cmd.exe 会话调用,%C4%9B%C5%A1%C4%8D%C5%99%C5%BE%C3%BD%C3%A1%C3%AD%C3%A9%C5%AF%C3%BA 将按原样工作 - 除非任何令牌成对的 % 个实例之间恰好是现有环境变量的名称(这似乎不太可能)。

  • 批处理 文件中,您必须 转义 % 个字符。通过加倍他们——见this answer;您可以通过对原始字符串应用以下 PowerShell 操作来获得它:

    '%C4%9B%C5%A1%C4%8D%C5%99%C5%BE%C3%BD%C3%A1%C3%AD%C3%A9%C5%AF%C3%BA' -replace '%', '%%'
    

Is it because of .NET object is employed?

  • 是的,powershell.exe,作为.NET的应用程序需要启动后者的运行时间(CLR),这在性能方面是不平凡的。

  • 此外,powershell.exe 默认加载其$PROFILE变量中列出的初始化文件,这可能需要额外的时间。

    • 通过 -NoProfile CLI 选项来抑制它。

Have also some C written, lightning fast urldecode.exe utility, but unfortunately it does not eat STDIN.

如果可行,将数据作为参数传递;例如:

urldecode.exe "%C4%9B%C5%A1%C4%8D%C5%99%C5%BE%C3%BD%C3%A1%C3%AD%C3%A9%C5%AF%C3%BA"

如果数据来自另一个命令的输出,可以使用for /f 先捕获一个变量,然后传递后者。


如果确实需要调用 powershell.exe,PowerShell 的 CLI,毕竟:

在优化性能方面您无能为力

  • 按照建议添加 -NoProfile
  • 将输入数据作为参数传递
  • 避免不必要的调用,例如 Write-Host 并依赖 PowerShell 的 隐式输出行为 [1]
powershell.exe -NoProfile -c "Add-Type -AssemblyName System.Web;[System.Web.HttpUtility]::UrlDecode('%C4%9B%C5%A1%C4%8D%C5%99%C5%BE%C3%BD%C3%A1%C3%AD%C3%A9%C5%AF%C3%BA')"

[1] 可选阅读:How do I output machine-parseable data from a PowerShell CLI call?

注意:示例命令假定为 运行 来自 cmd.exe / PowerShell 外部。

  • PowerShell 的 CLI 仅支持 text 作为输出,也不支持原始字节数据。

  • 为了输出数据供以后程序化处理,你可能必须明确确保输出的是什么是 机器可解析的 而不是 仅显示 .

两个基本选择:

  • 依靠 PowerShell 的默认输出格式 来输出 strings(文本)开始,以及 numbers - 尽管对于分数和非常大或非常小的非整数,可能需要额外的努力必填。

  • 明确使用基于文本的结构化数据格式,例如 CSV 或 Json , 表示复杂对象.


依赖 PowerShell 的默认输出格式:

  • 如果输出数据是本身文本(字符串),则不需要额外的努力。这适用于您的情况,因此只需 隐式 输出从 [System.Web.HttpUtility]::UrlDecode() 调用返回的字符串就足够了:

    # A simple example that outputs a 512-character string; note that 
    # printing to the _console_ (terminal) will insert line breaks for 
    # *display*, but the string data itself does _not_ contain any 
    # (other than a single _trailing one), irrespective of the 
    # console window width:
    powershell -noprofile -c "'x' * 512"
    
  • 如果输出数据包含数字,您可能必须应用明确的文化不变格式,如果您代码必须 运行 具有不同的有效文化:

    • True integer 类型 not 需要特殊处理,因为它们的字符串表示实际上是文化中立的。

    • 但是,分数 数字([double][decimal])确实使用当前文化的 小数点,后面的处理可能不会想到:

      # With, say, culture fr-FR (French) in effect, this outputs
      # "1,2", i.e. uses a *comma* as the decimal mark.
      powershell -noprofile -c "1.2"
      
      # Simple workaround: Let PowerShell *stringify* the number explicitly
      # in an *expandable* (interpolating) string, which uses
      # the *invariant culture* for formatting, where the decimal
      # mark is *always "." (dot).
      # The following outputs "1.2", irrespective of what culture is in effect.    
      powershell -noprofile -c " $num=1.2; \"$num\" "
      
    • 最后,非常大和非常小的 [double] 值会导致输出 指数 符号(例如,5.1E-07 for 0.00000051);为避免这种情况,需要 explicit number formatting,这可以通过 .ToString() 方法完成:

      # The following outputs 0.000051" in all cultures, as intended.
      powershell -noprofile -c "$n=0.000051; $n.ToString('F6', [cultureinfo]::InvariantCulture)"
      
  • 如果您想以机器可解析的形式输出复杂对象的表示,则需要做更多的工作,如下一节中讨论

    • 在这种情况下,依赖 PowerShell 的默认输出格式 不是 一个选项,因为隐式输出和(等效的显式 Write-Output 调用)导致CLI 应用 for-display-only 格式,这对 人类观察者 有意义,但不能稳健地 解析 .

      # Produces output helpful to the *human observer*, but isn't
      # designed for *parsing*.
      # `Get-ChildItem` outputs [System.IO.FileSystemInfo] objects.
      powershell -noprofile -c "Get-ChildItem /"
      
    • 请注意 Write-Host 的使用 不是 替代方法 : Write-Host 从根本上说不是为 data 输出而设计的,它为复杂对象创建的文本表示通常对 human 观察者甚至没有意义 - 参见 了解更多信息。


使用基于文本的结构化数据格式,例如 CSV 或 Json:

注:

  • 假设,最简单的方法是使用 CLI 的 -OutputFormat Xml 选项,它使用基于 XML 的 CLIXML 格式序列化输出,PowerShell 本身使用对于远程处理和后台作业 - 请参阅

  • 但是,这种格式只能被 PowerShell 本身理解,第三方应用程序要解析它,它们必须基于 .NET 并使用 PowerShell SDK

    • 此外,如果您使用指定的命令从 PowerShell调用另一个 PowerShell 实例,此格式将自动用于序列化和反序列化作为脚本块 ({ ... }) - 请参阅 。但是,很少需要从 PowerShell 本身调用 PowerShell CLI,直接调用 PowerShell 代码和脚本可提供完整的类型保真度以及更好的性能。
  • 最后,请注意 所有 序列化格式,包括下面讨论的 CSV 和 JSON,在忠实地表示数据,尽管 -OutputFormat Xml 最接近。

PowerShell 附带 ConvertTo-Csv and ConvertTo-Json 等 cmdlet,可以轻松将输出转换为结构化 CSV 和 JSON 格式。

在这种情况下使用 Get-Item call to get information about PowerShell's installation directory ($PSHOME) as an example; Get-Item outputs a System.IO.DirectoryInfo 实例:

C:\>powershell -noprofile -c "Get-Item $PSHOME | ConvertTo-Csv -NoTypeInformation"
"PSPath","PSParentPath","PSChildName","PSDrive","PSProvider","PSIsContainer","Mode","BaseName","Target","LinkType","Name","FullName","Parent","Exists","Root","Extension","CreationTime","CreationTimeUtc","LastAccessTime","LastAccessTimeUtc","LastWriteTime","LastWriteTimeUtc","Attributes"
"Microsoft.PowerShell.Core\FileSystem::C:\Windows\System32\WindowsPowerShell\v1.0","Microsoft.PowerShell.Core\FileSystem::C:\Windows\System32\WindowsPowerShell","v1.0","C","Microsoft.PowerShell.Core\FileSystem","True","d-----","v1.0","System.Collections.Generic.List`1[System.String]",,"v1.0","C:\Windows\System32\WindowsPowerShell\v1.0","WindowsPowerShell","True","C:\",".0","12/7/2019 4:14:52 AM","12/7/2019 9:14:52 AM","3/14/2021 10:33:10 AM","3/14/2021 2:33:10 PM","11/6/2020 3:52:41 AM","11/6/2020 8:52:41 AM","Directory"

注意:PowerShell (Core) 7+

不再需要 -NoTypeInformation
C:\>powershell -noprofile -c "Get-Item $PSHOME | ConvertTo-Json -Depth 1"
{
    "Name":  "v1.0",
    "FullName":  "C:\Windows\System32\WindowsPowerShell\v1.0",
    "Parent":  {
                   "Name":  "WindowsPowerShell",
                   "FullName":  "C:\Windows\System32\WindowsPowerShell",
                   "Parent":  "System32",
                   "Exists":  true,
                   "Root":  "C:\",
                   "Extension":  "",
                   "CreationTime":  "\/Date(1575710092565)\/",
                   "CreationTimeUtc":  "\/Date(1575710092565)\/",
                   "LastAccessTime":  "\/Date(1615733476841)\/",
                   "LastAccessTimeUtc":  "\/Date(1615733476841)\/",
                   "LastWriteTime":  "\/Date(1575710092565)\/",
                   "LastWriteTimeUtc":  "\/Date(1575710092565)\/",
                   "Attributes":  16
               },
    "Exists":  true
    // ...
}

由于JSON是分层数据格式,序列化深度必须限制在-Depth以防止在序列化任意.NET类型时出现“运行away”序列化;对于仅由原始 .NET 类型组成的 [pscustomobject][hashtable] 对象图,这不是必需的。