Powershell编码默认输出

时间:2018-01-29 13:31:13

标签: powershell encoding tfs

我在TFS构建中运行的powershell脚本存在以下问题。这两个问题都与TFS无关,可以使用简单的PowerShell命令行窗口重现。

1)与TFS完全无关。在管道方面,似乎Powershell不喜欢德国变形金刚。

1a)这行代码工作正常,所有变音符号都正确显示

.\TF.exe hist "$/Test" /recursive /collection:https://TestTFS/tfs/TestCollection /noprompt /version:C1~T

1b)这条线与变音符号混淆

.\TF.exe hist "$/Test" /recursive /collection:https://TestTFS/tfs/TestCollection /noprompt /version:C1~T | Out-String

最初我尝试过Out-File并且仅将编码更改为每个排版中的变音符号编码错误(UTF8,unicode,UTF32,...)

我真的不知道如何从标准输出中提取字符串并使变音符合适。

2)当使用Out-File或Out-String时,输出中的每一行在80个字符后被截断,似乎是默认的屏幕缓冲区设置。如何在powershell脚本中更改它,以及为什么在重定向输出时它甚至会产生影响。

2 个答案:

答案 0 :(得分:0)

问题2不是Powershell问题。 tfs documentation表示关注默认/format参数(即/format:brief

  

某些数据可能会被截断。

/format:detailed没有该警告,但会返回更多信息,您可以在执行Out-StringOut-File之前使用Powershell处理这些信息。

答案 1 :(得分:0)

tl;dr

以下应该解决您的两个问题,这些问题源于 tf.exe 使用 ANSI 字符编码而不是预期的 OEM 编码,以及默认情况下截断输出.:

  • 如果您使用的是 Windows PowerShell(仅限 Windows 的旧版 PowerShell,版本最高为 v5.1):
$correctlyCapturedOutput = 
  & {
    $prev = [Console]::OutputEncoding
    [Console]::OutputEncoding = [System.Text.Encoding]::Default

    # Note the addition of /format:detailed
    .\tf.exe hist '$/Test' /recursive /collection:https://TestTFS/tfs/TestCollection /noprompt /format:detailed /version:C1~T

    [Console]::OutputEncoding = $prev
  }
  • 如果您使用的是跨平台、按需安装PowerShell (Core) 7+

    • 注意:[System.Text.Encoding]::Default 在 Windows PowerShell 中报告活动 ANSI 代码页的编码,在 PowerShell (Core) 中报告(无 BOM)UTF-8(反映 .NET Core 的 / .NET 5+ 的行为)。因此,必须明确确定活动的 ANSI 代码页,最可靠的方法是通过注册表完成。
$correctlyCapturedOutput = 
  & {
    $prev = [Console]::OutputEncoding
    [Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding(
      [int] ((Get-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Control\Nls\CodePage ACP).ACP)
    )

    # Note the addition of /format:detailed
    .\tf.exe hist '$/Test' /recursive /collection:https://TestTFS/tfs/TestCollection /noprompt /format:detailed /version:C1~T

    [Console]::OutputEncoding = $prev
  }

This Gist 包含 helper 函数 Invoke-WithEncoding,它可以在两个 PowerShell 版本中将上述简化如下:

$correctlyCapturedOutput = 
  Invoke-WithEncoding -Encoding Ansi {
    .\tf.exe hist '$/Test' /recursive /collection:https://TestTFS/tfs/TestCollection /noprompt /format:detailed /version:C1~T
  }

你可以直接用下面的命令下载并定义函数(虽然我个人可以向你保证这样做是安全的,但最好先检查源代码):

# Downloads and defines function Invoke-WithEncoding in the current session.
irm https://gist.github.com/mklement0/ef57aea441ea8bd43387a7d7edfc6c19/raw/Invoke-WithEncoding.ps1 | iex

继续阅读以进行详细讨论。


重新变音(字符编码)问题:

虽然外部程序的输出可以打印控制台,但是当涉及到在变量中捕获输出或重定向它时< /em> - 例如在您的情况下通过管道将其发送到 Out-String - PowerShell 使用存储在中的字符编码将输出解码为 .NET 字符串 [Console]::OutputEncoding

如果 [Console]::OutputEncoding 与外部程序使用的实际编码不匹配,PowerShell 将误解输出。

解决方案是(临时)将[Console]::OutputEncoding设置为外部程序使用的实际编码

虽然 official tf.exe documentation 不讨论字符编码,但此 comment on GitHub 表明 tf.exe 使用系统的活动 ANSI 代码页,例如美国英语或西欧的 Windows-1252系统。

应该注意,ANSI 代码页的使用是控制台应用程序的非标准行为 ,因为控制台应用程序应该使用系统的活动 OEM 代码页。顺便说一句:python 在默认情况下也表现出这种非标准行为,尽管其行为是可配置的。

顶部的解决方案展示了如何将 [Console]::OutputEncoding 临时切换到活动的 ANSI 代码页的编码,以确保 PowerShell 正确解码 tf.exe 的输出。


使用 Out-String / Out-File 重新截断输出行(因此还有 >>>):

  • 正如 Mustafa Zengin's helpful answer 指出的那样,在您的特定情况下 - 由于使用了 tf.exe - 截断发生在 ,即 tf.exe 本身按照其默认格式输出截断数据(当还指定 /format:brief 时隐含 /noprompt)。

  • 一般来说,Out-StringOut-File / > / >>根据情况截断或换行基于控制台窗口宽度的输出行(在没有控制台的情况下默认为 120 个字符):

    • 换行的截断仅适用于源自 PowerShell 丰富的输出格式系统生成的非原始、非字符串对象表示的输出行:

    • 字符串本身[string] 输入)以及.NET 原始类型的字符串表示(加上一些更多的单值类型)< /em> 不会被截断/换行。

  • 由于 PowerShell 只将 外部程序 的输出解释为 text[string] 实例),因此截断/换行会 em>不会发生

    • 因此,通常没有理由在外部程序输出上使用 Out-String - 除非您需要连接输出行的流(数组)以形成一个 , 多行字符串用于进一步的内存处理。
    • 但是,请注意Out-String 总是在结果字符串中添加一个尾随换行,这可能是我们不希望看到的;使用 (...) -join [Environment]::NewLine 来避免这种情况; Out-String 的问题行为在 GitHub issue #14444 中讨论。